@nebutra/design-sync 2.0.0 → 3.0.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 +14 -31
- package/dist/cli/index.js +2 -2
- package/dist/detect.d.ts.map +1 -1
- package/dist/detect.js +2 -25
- package/dist/factory.d.ts +3 -14
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +6 -26
- package/dist/index.d.ts +1 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -6
- package/dist/io.js +2 -2
- package/dist/serialize/to-preview-html.d.ts.map +1 -1
- package/dist/serialize/to-preview-html.js +15 -9
- package/dist/serialize/to-preview-html.template.d.ts +2 -2
- package/dist/serialize/to-preview-html.template.js +2 -2
- package/dist/types.d.ts +4 -28
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -18
- package/src/cli/index.ts +2 -2
- package/dist/figma-config/index.d.ts +0 -37
- package/dist/figma-config/index.d.ts.map +0 -1
- package/dist/figma-config/index.js +0 -12
- package/dist/figma-config/tokens-studio.config.json +0 -34
- package/dist/providers/figma.d.ts +0 -21
- package/dist/providers/figma.d.ts.map +0 -1
- package/dist/providers/figma.js +0 -179
- package/dist/providers/penpot.d.ts +0 -21
- package/dist/providers/penpot.d.ts.map +0 -1
- package/dist/providers/penpot.js +0 -137
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
> **Status: Foundation** — provider interface, detection, git-only path, and CLI are complete.
|
|
1
|
+
> **Status: Foundation** — provider interface, detection, git-only path, and CLI are complete. See `DESIGN.md` for the design rationale.
|
|
2
2
|
|
|
3
3
|
# @nebutra/design-sync
|
|
4
4
|
|
|
@@ -8,9 +8,8 @@ Provider-agnostic design-tool sync. The application code never imports a specifi
|
|
|
8
8
|
|
|
9
9
|
| Provider | Customer profile | Cost | China-friendly | Status |
|
|
10
10
|
|----------|------------------|------|----------------|--------|
|
|
11
|
-
| `figma` | North-American / global teams with a Figma seat (Tokens Studio plugin) | Figma + Tokens Studio | No (Figma blocked) | dry-run, pull works |
|
|
12
|
-
| `penpot` | Self-hosted / privacy-first / China compliance | Free / self-host | Yes (self-hostable) | dry-run scaffold |
|
|
13
11
|
| `git-only` | Indie hackers, AI-driven dev workflows, "no designer" teams | Free | Yes | full |
|
|
12
|
+
| `design-md` | AI-native workflows — DESIGN.md as the design surface | Free | Yes | full |
|
|
14
13
|
| `memory` | CI / unit tests | Free | n/a | full (test fixture) |
|
|
15
14
|
|
|
16
15
|
## Quick start
|
|
@@ -18,14 +17,14 @@ Provider-agnostic design-tool sync. The application code never imports a specifi
|
|
|
18
17
|
```ts
|
|
19
18
|
import { getDesignSync } from "@nebutra/design-sync";
|
|
20
19
|
|
|
21
|
-
// Auto-detects the provider from environment variables.
|
|
20
|
+
// Auto-detects the provider from environment variables (defaults to git-only).
|
|
22
21
|
const sync = await getDesignSync();
|
|
23
22
|
|
|
24
23
|
// Pull design-tool → repo (DTCG JSON files under packages/design/design-tokens/tokens).
|
|
25
24
|
const result = await sync.pull();
|
|
26
25
|
console.log(result.summary);
|
|
27
26
|
|
|
28
|
-
// Push repo → design-tool. Defaults to dry-run on
|
|
27
|
+
// Push repo → design-tool. Defaults to dry-run on design-md
|
|
29
28
|
// until you opt in by providing credentials.
|
|
30
29
|
await sync.push({ dryRun: true });
|
|
31
30
|
|
|
@@ -37,10 +36,8 @@ const status = await sync.healthcheck();
|
|
|
37
36
|
|
|
38
37
|
| Priority | Condition | Provider |
|
|
39
38
|
|----------|-----------|----------|
|
|
40
|
-
| 1 | `DESIGN_SYNC_PROVIDER` set to `
|
|
41
|
-
| 2 |
|
|
42
|
-
| 3 | `PENPOT_API_URL` **and** `PENPOT_TOKEN` present | `penpot` |
|
|
43
|
-
| 4 | fallback | `git-only` |
|
|
39
|
+
| 1 | `DESIGN_SYNC_PROVIDER` set to `git-only` / `design-md` / `memory` | as specified |
|
|
40
|
+
| 2 | fallback | `git-only` |
|
|
44
41
|
|
|
45
42
|
`memory` is never auto-detected; it must be requested explicitly (used in tests).
|
|
46
43
|
|
|
@@ -48,19 +45,10 @@ const status = await sync.healthcheck();
|
|
|
48
45
|
|
|
49
46
|
```env
|
|
50
47
|
# Optional — force a specific provider
|
|
51
|
-
DESIGN_SYNC_PROVIDER="" #
|
|
52
|
-
|
|
53
|
-
#
|
|
54
|
-
|
|
55
|
-
FIGMA_FILE_ID="" # the :file_key segment of the Figma URL
|
|
56
|
-
FIGMA_GITHUB_REPO="Nebutra/Nebutra-Sailor"
|
|
57
|
-
FIGMA_GITHUB_BRANCH="main"
|
|
58
|
-
|
|
59
|
-
# Penpot
|
|
60
|
-
PENPOT_API_URL="https://design.penpot.app/api" # or your self-host URL
|
|
61
|
-
PENPOT_TOKEN=""
|
|
62
|
-
PENPOT_FILE_ID=""
|
|
63
|
-
PENPOT_TEAM_ID=""
|
|
48
|
+
DESIGN_SYNC_PROVIDER="" # git-only | design-md | memory
|
|
49
|
+
|
|
50
|
+
# design-md
|
|
51
|
+
DESIGN_MD_PATH="" # defaults to <cwd>/DESIGN.md
|
|
64
52
|
```
|
|
65
53
|
|
|
66
54
|
## CLI
|
|
@@ -77,7 +65,7 @@ pnpm --filter @nebutra/design-sync exec design-sync healthcheck
|
|
|
77
65
|
# Pull design-tool → repo.
|
|
78
66
|
pnpm --filter @nebutra/design-sync exec design-sync pull
|
|
79
67
|
|
|
80
|
-
# Push repo → design-tool (defaults to dry-run on
|
|
68
|
+
# Push repo → design-tool (defaults to dry-run on design-md).
|
|
81
69
|
pnpm --filter @nebutra/design-sync exec design-sync push --dry-run
|
|
82
70
|
|
|
83
71
|
# Restrict to specific token sets.
|
|
@@ -92,9 +80,8 @@ pnpm --filter @nebutra/design-sync exec design-sync detect --json
|
|
|
92
80
|
|
|
93
81
|
## Choosing a provider
|
|
94
82
|
|
|
95
|
-
- **You have a Figma file + designers** → use `figma`. The Tokens Studio plugin owns the git transport (push/pull DTCG to a GitHub branch). The provider validates `.tokens-studio/` metadata and re-reads the DTCG mirror written by the plugin.
|
|
96
|
-
- **You self-host or operate inside mainland China** → use `penpot`. It speaks DTCG natively and can be self-hosted; the provider exposes the same `pull/push/healthcheck` surface so you migrate by flipping one env var.
|
|
97
83
|
- **You ship without a design tool (indie hacker, AI-first dev)** → use `git-only`. Zero config. The DTCG files under `packages/design/design-tokens/tokens` *are* the source of truth.
|
|
84
|
+
- **You want an AI-native design surface** → use `design-md`. Push/pull a `DESIGN.md` file (markdown + YAML front matter) that models and humans can both read and edit directly, with an official lint gate before writes.
|
|
98
85
|
- **You write tests** → inject `MemoryProvider` directly via `setDesignSync(...)`.
|
|
99
86
|
|
|
100
87
|
## How DTCG flows through the package
|
|
@@ -104,14 +91,10 @@ packages/design/design-tokens/tokens/*.json ← single source of truth (W3C DT
|
|
|
104
91
|
▲
|
|
105
92
|
│ pull() / push()
|
|
106
93
|
▼
|
|
107
|
-
DesignSyncProvider (
|
|
94
|
+
DesignSyncProvider (git-only | design-md | memory)
|
|
108
95
|
│
|
|
109
96
|
▼
|
|
110
|
-
|
|
97
|
+
DESIGN.md or local files (git-only)
|
|
111
98
|
```
|
|
112
99
|
|
|
113
100
|
Every provider goes through the same DTCG validator (`validateDtcgTree`) before sending data anywhere; bad token files fail closed before they reach a remote API.
|
|
114
|
-
|
|
115
|
-
## Migration from `.tokens-studio/`
|
|
116
|
-
|
|
117
|
-
The legacy Tokens Studio config (`.tokens-studio/{config,metadata,themes}.json`) is preserved at the repo root for the plugin to discover, **and** mirrored into `src/figma-config/tokens-studio.config.json` so the figma provider self-documents. Update both files in lock-step if the plugin schema changes.
|
package/dist/cli/index.js
CHANGED
|
@@ -86,12 +86,12 @@ Commands:
|
|
|
86
86
|
detect Print the resolved provider + env diagnostics
|
|
87
87
|
healthcheck Run the provider's healthcheck()
|
|
88
88
|
pull Pull design-tool → repo (DTCG JSON)
|
|
89
|
-
push Push repo → design-tool (defaults to dry-run on
|
|
89
|
+
push Push repo → design-tool (defaults to dry-run on design-md)
|
|
90
90
|
brand Pull tokens then compile a Brand Package (Create Center)
|
|
91
91
|
help Show this message
|
|
92
92
|
|
|
93
93
|
Options:
|
|
94
|
-
--provider <X> Override provider (
|
|
94
|
+
--provider <X> Override provider (git-only|memory|design-md)
|
|
95
95
|
--themes a,b Restrict to specific token sets
|
|
96
96
|
--id <id> Brand package id (for brand command)
|
|
97
97
|
--name <name> Brand package display name
|
package/dist/detect.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAmBtD;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,sBAAsB,GAAG,SAAS,CAOpC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,sBAAsB,CAK3F;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG;IACjE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,sBAAsB,CAAC;CAClC,CAgBA"}
|
package/dist/detect.js
CHANGED
|
@@ -3,16 +3,12 @@
|
|
|
3
3
|
// =============================================================================
|
|
4
4
|
// Resolution order (highest priority first):
|
|
5
5
|
// 1. DESIGN_SYNC_PROVIDER env var (explicit)
|
|
6
|
-
// 2.
|
|
7
|
-
// 3. PENPOT_API_URL + PENPOT_TOKEN → "penpot"
|
|
8
|
-
// 4. fallback → "git-only"
|
|
6
|
+
// 2. fallback → "git-only"
|
|
9
7
|
//
|
|
10
8
|
// `memory` and `design-md` are never auto-detected; they must be requested
|
|
11
9
|
// explicitly via the DESIGN_SYNC_PROVIDER env var or a config object.
|
|
12
10
|
// =============================================================================
|
|
13
11
|
const VALID_PROVIDERS = new Set([
|
|
14
|
-
"figma",
|
|
15
|
-
"penpot",
|
|
16
12
|
"git-only",
|
|
17
13
|
"memory",
|
|
18
14
|
"design-md",
|
|
@@ -38,14 +34,6 @@ export function detectProvider(env = process.env) {
|
|
|
38
34
|
const explicit = readConfiguredProvider(env);
|
|
39
35
|
if (explicit)
|
|
40
36
|
return explicit;
|
|
41
|
-
const figmaToken = env.FIGMA_PERSONAL_ACCESS_TOKEN?.trim();
|
|
42
|
-
const figmaFileId = env.FIGMA_FILE_ID?.trim();
|
|
43
|
-
if (figmaToken && figmaFileId)
|
|
44
|
-
return "figma";
|
|
45
|
-
const penpotUrl = env.PENPOT_API_URL?.trim();
|
|
46
|
-
const penpotToken = env.PENPOT_TOKEN?.trim();
|
|
47
|
-
if (penpotUrl && penpotToken)
|
|
48
|
-
return "penpot";
|
|
49
37
|
return "git-only";
|
|
50
38
|
}
|
|
51
39
|
/**
|
|
@@ -55,18 +43,7 @@ export function detectProvider(env = process.env) {
|
|
|
55
43
|
export function describeEnv(env = process.env) {
|
|
56
44
|
const detected = [];
|
|
57
45
|
const missing = [];
|
|
58
|
-
const known = [
|
|
59
|
-
"DESIGN_SYNC_PROVIDER",
|
|
60
|
-
"FIGMA_PERSONAL_ACCESS_TOKEN",
|
|
61
|
-
"FIGMA_FILE_ID",
|
|
62
|
-
"FIGMA_GITHUB_REPO",
|
|
63
|
-
"FIGMA_GITHUB_BRANCH",
|
|
64
|
-
"PENPOT_API_URL",
|
|
65
|
-
"PENPOT_TOKEN",
|
|
66
|
-
"PENPOT_FILE_ID",
|
|
67
|
-
"PENPOT_TEAM_ID",
|
|
68
|
-
"DESIGN_MD_PATH",
|
|
69
|
-
];
|
|
46
|
+
const known = ["DESIGN_SYNC_PROVIDER", "DESIGN_MD_PATH"];
|
|
70
47
|
for (const key of known) {
|
|
71
48
|
if (env[key]?.trim())
|
|
72
49
|
detected.push(key);
|
package/dist/factory.d.ts
CHANGED
|
@@ -7,22 +7,11 @@ import type { DesignSyncConfig, DesignSyncProvider } from "./types";
|
|
|
7
7
|
* // Auto-detect from environment
|
|
8
8
|
* const sync = await createDesignSync();
|
|
9
9
|
*
|
|
10
|
-
* // Explicit Figma
|
|
11
|
-
* const sync = await createDesignSync({
|
|
12
|
-
* provider: "figma",
|
|
13
|
-
* personalAccessToken: process.env.FIGMA_PERSONAL_ACCESS_TOKEN,
|
|
14
|
-
* fileId: "abc123",
|
|
15
|
-
* });
|
|
16
|
-
*
|
|
17
|
-
* // Explicit Penpot
|
|
18
|
-
* const sync = await createDesignSync({
|
|
19
|
-
* provider: "penpot",
|
|
20
|
-
* apiUrl: "https://design.penpot.app/api",
|
|
21
|
-
* token: process.env.PENPOT_TOKEN,
|
|
22
|
-
* });
|
|
23
|
-
*
|
|
24
10
|
* // Explicit git-only (zero config)
|
|
25
11
|
* const sync = await createDesignSync({ provider: "git-only" });
|
|
12
|
+
*
|
|
13
|
+
* // Explicit design-md
|
|
14
|
+
* const sync = await createDesignSync({ provider: "design-md" });
|
|
26
15
|
* ```
|
|
27
16
|
*/
|
|
28
17
|
export declare function createDesignSync(config?: DesignSyncConfig): Promise<DesignSyncProvider>;
|
package/dist/factory.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAA0B,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAA0B,MAAM,SAAS,CAAC;AAgB5F;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA2B7F;AAED;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAKjE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAEhE;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAEtC"}
|
package/dist/factory.js
CHANGED
|
@@ -6,9 +6,10 @@ import { detectProvider } from "./detect";
|
|
|
6
6
|
// The factory resolves the correct provider at runtime based on:
|
|
7
7
|
// 1. Explicit config passed to `createDesignSync()`
|
|
8
8
|
// 2. `DESIGN_SYNC_PROVIDER` environment variable
|
|
9
|
-
// 3.
|
|
9
|
+
// 3. fallback → git-only
|
|
10
10
|
//
|
|
11
|
-
// This lets customers switch
|
|
11
|
+
// This lets customers switch between the git-only workflow and the AI-native
|
|
12
|
+
// DESIGN.md serialization without changing application code.
|
|
12
13
|
// =============================================================================
|
|
13
14
|
let defaultProvider = null;
|
|
14
15
|
/**
|
|
@@ -19,38 +20,17 @@ let defaultProvider = null;
|
|
|
19
20
|
* // Auto-detect from environment
|
|
20
21
|
* const sync = await createDesignSync();
|
|
21
22
|
*
|
|
22
|
-
* // Explicit Figma
|
|
23
|
-
* const sync = await createDesignSync({
|
|
24
|
-
* provider: "figma",
|
|
25
|
-
* personalAccessToken: process.env.FIGMA_PERSONAL_ACCESS_TOKEN,
|
|
26
|
-
* fileId: "abc123",
|
|
27
|
-
* });
|
|
28
|
-
*
|
|
29
|
-
* // Explicit Penpot
|
|
30
|
-
* const sync = await createDesignSync({
|
|
31
|
-
* provider: "penpot",
|
|
32
|
-
* apiUrl: "https://design.penpot.app/api",
|
|
33
|
-
* token: process.env.PENPOT_TOKEN,
|
|
34
|
-
* });
|
|
35
|
-
*
|
|
36
23
|
* // Explicit git-only (zero config)
|
|
37
24
|
* const sync = await createDesignSync({ provider: "git-only" });
|
|
25
|
+
*
|
|
26
|
+
* // Explicit design-md
|
|
27
|
+
* const sync = await createDesignSync({ provider: "design-md" });
|
|
38
28
|
* ```
|
|
39
29
|
*/
|
|
40
30
|
export async function createDesignSync(config) {
|
|
41
31
|
const providerType = config?.provider ?? detectProvider();
|
|
42
32
|
logger.info("[design-sync] Creating provider", { provider: providerType });
|
|
43
33
|
switch (providerType) {
|
|
44
|
-
case "figma": {
|
|
45
|
-
const { FigmaProvider } = await import("./providers/figma");
|
|
46
|
-
const figmaConfig = config?.provider === "figma" ? config : undefined;
|
|
47
|
-
return new FigmaProvider(figmaConfig ?? { provider: "figma" });
|
|
48
|
-
}
|
|
49
|
-
case "penpot": {
|
|
50
|
-
const { PenpotProvider } = await import("./providers/penpot");
|
|
51
|
-
const penpotConfig = config?.provider === "penpot" ? config : undefined;
|
|
52
|
-
return new PenpotProvider(penpotConfig ?? { provider: "penpot" });
|
|
53
|
-
}
|
|
54
34
|
case "git-only": {
|
|
55
35
|
const { GitOnlyProvider } = await import("./providers/git-only");
|
|
56
36
|
const gitConfig = config?.provider === "git-only" ? config : undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
export { describeEnv, detectProvider, readConfiguredProvider } from "./detect";
|
|
2
2
|
export { createDesignSync, getDesignSync, resetDesignSync, setDesignSync, } from "./factory";
|
|
3
|
-
export type { FigmaTokensStudioConfig } from "./figma-config/index";
|
|
4
|
-
export { FIGMA_TOKENS_STUDIO_CONFIG } from "./figma-config/index";
|
|
5
3
|
export { defaultTokensDir, defaultTokensStudioDir, readTokenSets, validateDtcgTree, writeTokenSet, } from "./io";
|
|
6
4
|
export type { DesignMdProvider } from "./providers/design-md";
|
|
7
|
-
export { FigmaProvider } from "./providers/figma";
|
|
8
5
|
export { GitOnlyProvider } from "./providers/git-only";
|
|
9
6
|
export { MemoryProvider } from "./providers/memory";
|
|
10
|
-
export { PenpotProvider } from "./providers/penpot";
|
|
11
7
|
export { compileBrandFromTokenSets, mergeTokenTrees, pullAndCompileBrand, serializeToBrandCss, type ToBrandPackageOptions, tokenSetsToReferoShape, } from "./serialize/to-brand-package";
|
|
12
8
|
export { serializeToDesignMd, type ToDesignMdOptions } from "./serialize/to-design-md";
|
|
13
9
|
export { serializeToPreviewHtml, type ToPreviewHtmlOptions, } from "./serialize/to-preview-html";
|
|
14
|
-
export type { BaseProviderConfig, DesignMdProviderConfig, DesignSyncConfig, DesignSyncProvider, DesignSyncProviderType, DesignTokenLeaf, DesignTokenSet, DesignTokenTree,
|
|
10
|
+
export type { BaseProviderConfig, DesignMdProviderConfig, DesignSyncConfig, DesignSyncProvider, DesignSyncProviderType, DesignTokenLeaf, DesignTokenSet, DesignTokenTree, GitOnlyProviderConfig, HealthStatus, MemoryProviderConfig, PullOptions, PullResult, PushOptions, PushResult, } from "./types";
|
|
15
11
|
export { DesignTokenLeafSchema } from "./types";
|
|
16
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAE/E,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,aAAa,GACd,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,aAAa,EACb,gBAAgB,EAChB,aAAa,GACd,MAAM,MAAM,CAAC;AASd,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,EACL,yBAAyB,EACzB,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,KAAK,qBAAqB,EAC1B,sBAAsB,GACvB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAE,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAEvF,OAAO,EACL,sBAAsB,EACtB,KAAK,oBAAoB,GAC1B,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,kBAAkB,EAClB,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,eAAe,EACf,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,YAAY,EACZ,oBAAoB,EACpB,WAAW,EACX,UAAU,EACV,WAAW,EACX,UAAU,GACX,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
// @nebutra/design-sync — Provider-agnostic design-tool sync
|
|
3
3
|
// =============================================================================
|
|
4
4
|
// Supports:
|
|
5
|
-
// - Figma + Tokens Studio (DTCG via GitHub, plugin owns the transport)
|
|
6
|
-
// - Penpot (REST API, self-hostable, China-friendly)
|
|
7
5
|
// - git-only (zero-config default — DTCG files only)
|
|
8
6
|
// - memory (test fixture)
|
|
9
7
|
// - design-md (AI-native DESIGN.md, markdown + YAML front matter)
|
|
@@ -20,14 +18,10 @@
|
|
|
20
18
|
export { describeEnv, detectProvider, readConfiguredProvider } from "./detect";
|
|
21
19
|
// ── Factory ─────────────────────────────────────────────────────────────────
|
|
22
20
|
export { createDesignSync, getDesignSync, resetDesignSync, setDesignSync, } from "./factory";
|
|
23
|
-
// ── Figma config snapshot ───────────────────────────────────────────────────
|
|
24
|
-
export { FIGMA_TOKENS_STUDIO_CONFIG } from "./figma-config/index";
|
|
25
21
|
// ── DTCG I/O helpers (re-exported for advanced consumers) ───────────────────
|
|
26
22
|
export { defaultTokensDir, defaultTokensStudioDir, readTokenSets, validateDtcgTree, writeTokenSet, } from "./io";
|
|
27
|
-
export { FigmaProvider } from "./providers/figma";
|
|
28
23
|
export { GitOnlyProvider } from "./providers/git-only";
|
|
29
24
|
export { MemoryProvider } from "./providers/memory";
|
|
30
|
-
export { PenpotProvider } from "./providers/penpot";
|
|
31
25
|
// ── DTCG → Brand Package (Create Center) ───────────────────────────────────
|
|
32
26
|
export { compileBrandFromTokenSets, mergeTokenTrees, pullAndCompileBrand, serializeToBrandCss, tokenSetsToReferoShape, } from "./serialize/to-brand-package";
|
|
33
27
|
// ── DTCG → DESIGN.md serializer ────────────────────────────────────────────
|
package/dist/io.js
CHANGED
|
@@ -5,8 +5,8 @@ import { DesignTokenLeafSchema } from "./types";
|
|
|
5
5
|
// DTCG Filesystem I/O
|
|
6
6
|
// =============================================================================
|
|
7
7
|
// Shared helpers used by every provider. Reading and writing the canonical
|
|
8
|
-
// W3C DTCG JSON tree on disk is identical across
|
|
9
|
-
//
|
|
8
|
+
// W3C DTCG JSON tree on disk is identical across every provider — the only
|
|
9
|
+
// thing that differs is what the provider does AFTER it has the data.
|
|
10
10
|
// =============================================================================
|
|
11
11
|
const TOKEN_FILE_EXT = ".json";
|
|
12
12
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"to-preview-html.d.ts","sourceRoot":"","sources":["../../src/serialize/to-preview-html.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;
|
|
1
|
+
{"version":3,"file":"to-preview-html.d.ts","sourceRoot":"","sources":["../../src/serialize/to-preview-html.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAkB/C,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,cAAc,EAAE,EACtB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,MAAM,CA2CR"}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* PURE: no filesystem I/O, no @google/design.md dependency.
|
|
15
15
|
* Only imports from `../types`, `./to-design-md.resolve`, and `./to-preview-html.template`.
|
|
16
16
|
*/
|
|
17
|
+
import { tokenColor } from "@nebutra/tokens/values";
|
|
17
18
|
import { buildIndex, buildIndexForSet, buildRounded, buildTypography, resolveColorRoles, } from "./to-design-md.resolve";
|
|
18
19
|
import { buildDocument, cssAttrValue, escapeHtml, } from "./to-preview-html.template";
|
|
19
20
|
/**
|
|
@@ -61,16 +62,21 @@ export function serializeToPreviewHtml(sets, options) {
|
|
|
61
62
|
});
|
|
62
63
|
}
|
|
63
64
|
// ─── CSS variable builder ──────────────────────────────────────────────────────
|
|
64
|
-
|
|
65
|
+
/**
|
|
66
|
+
* A role the DESIGN.md does not set falls back to the House token for that
|
|
67
|
+
* mode, read from @nebutra/tokens/values — the value the apps render — rather
|
|
68
|
+
* than a hex restated here (the old dark fallbacks were pre-House Slate).
|
|
69
|
+
*/
|
|
70
|
+
function buildCssVars(colors, mode) {
|
|
65
71
|
return {
|
|
66
|
-
primary: colors.primary ?? "
|
|
67
|
-
accent: colors.accent ?? "
|
|
68
|
-
tertiary: colors.tertiary ?? "
|
|
69
|
-
danger: colors.danger ?? "
|
|
70
|
-
warning: colors.warning ?? "
|
|
71
|
-
success: colors.success ?? "
|
|
72
|
-
background: colors.background ?? (
|
|
73
|
-
foreground: colors.foreground ?? (
|
|
72
|
+
primary: colors.primary ?? tokenColor("--brand-primary", mode),
|
|
73
|
+
accent: colors.accent ?? tokenColor("--brand-accent", mode),
|
|
74
|
+
tertiary: colors.tertiary ?? tokenColor("--brand-tertiary", mode),
|
|
75
|
+
danger: colors.danger ?? tokenColor("--status-danger", mode),
|
|
76
|
+
warning: colors.warning ?? tokenColor("--status-warning", mode),
|
|
77
|
+
success: colors.success ?? tokenColor("--status-success", mode),
|
|
78
|
+
background: colors.background ?? tokenColor("--background", mode),
|
|
79
|
+
foreground: colors.foreground ?? tokenColor("--foreground", mode),
|
|
74
80
|
};
|
|
75
81
|
}
|
|
76
82
|
// ─── Section builders ──────────────────────────────────────────────────────────
|
|
@@ -29,8 +29,8 @@ export declare function escapeHtml(value: string): string;
|
|
|
29
29
|
* remaining angle brackets become `<`/`>`.
|
|
30
30
|
*
|
|
31
31
|
* Example:
|
|
32
|
-
* cssAttrValue('"Geist", "
|
|
33
|
-
* → '"Geist", "
|
|
32
|
+
* cssAttrValue('"Geist", "MiSans", sans-serif')
|
|
33
|
+
* → '"Geist", "MiSans", sans-serif'
|
|
34
34
|
*
|
|
35
35
|
* cssAttrValue('red;display:none')
|
|
36
36
|
* → 'reddisplay:none' (semicolon stripped before HTML-escape)
|
|
@@ -37,8 +37,8 @@ export function escapeHtml(value) {
|
|
|
37
37
|
* remaining angle brackets become `<`/`>`.
|
|
38
38
|
*
|
|
39
39
|
* Example:
|
|
40
|
-
* cssAttrValue('"Geist", "
|
|
41
|
-
* → '"Geist", "
|
|
40
|
+
* cssAttrValue('"Geist", "MiSans", sans-serif')
|
|
41
|
+
* → '"Geist", "MiSans", sans-serif'
|
|
42
42
|
*
|
|
43
43
|
* cssAttrValue('red;display:none')
|
|
44
44
|
* → 'reddisplay:none' (semicolon stripped before HTML-escape)
|
package/dist/types.d.ts
CHANGED
|
@@ -2,13 +2,11 @@ import { z } from "zod";
|
|
|
2
2
|
/**
|
|
3
3
|
* Supported design-sync backend providers.
|
|
4
4
|
*
|
|
5
|
-
* - `figma` — Figma + Tokens Studio plugin (DTCG via GitHub provider sync)
|
|
6
|
-
* - `penpot` — Penpot REST API (self-hostable, China-friendly)
|
|
7
5
|
* - `git-only` — No design tool; reads/writes local DTCG files directly
|
|
8
6
|
* - `memory` — In-memory test fixture (CI / unit tests only)
|
|
9
7
|
* - `design-md` — AI-native DESIGN.md format (@google/design.md) — markdown + YAML front matter
|
|
10
8
|
*/
|
|
11
|
-
export type DesignSyncProviderType = "
|
|
9
|
+
export type DesignSyncProviderType = "git-only" | "memory" | "design-md";
|
|
12
10
|
/**
|
|
13
11
|
* A W3C Design Tokens Community Group (DTCG) leaf token.
|
|
14
12
|
* Every leaf MUST carry both `$value` and `$type`.
|
|
@@ -43,7 +41,7 @@ export interface DesignTokenSet {
|
|
|
43
41
|
export interface PullOptions {
|
|
44
42
|
/**
|
|
45
43
|
* Optional theme/token-set filter. When omitted, all sets are pulled.
|
|
46
|
-
* Provider-specific naming applies
|
|
44
|
+
* Provider-specific naming applies.
|
|
47
45
|
*/
|
|
48
46
|
themes?: string[];
|
|
49
47
|
/**
|
|
@@ -59,7 +57,7 @@ export interface PushOptions {
|
|
|
59
57
|
themes?: string[];
|
|
60
58
|
/**
|
|
61
59
|
* If true, the provider should compute the diff but NOT call the remote API.
|
|
62
|
-
* `
|
|
60
|
+
* `design-md.push({ dryRun: true })` is the default until the user opts in.
|
|
63
61
|
*/
|
|
64
62
|
dryRun?: boolean;
|
|
65
63
|
/** Tenant scoping */
|
|
@@ -142,28 +140,6 @@ export interface BaseProviderConfig {
|
|
|
142
140
|
*/
|
|
143
141
|
tokensStudioDir?: string;
|
|
144
142
|
}
|
|
145
|
-
export interface FigmaProviderConfig extends BaseProviderConfig {
|
|
146
|
-
provider: "figma";
|
|
147
|
-
/** Figma personal access token. Defaults to `process.env.FIGMA_PERSONAL_ACCESS_TOKEN`. */
|
|
148
|
-
personalAccessToken?: string;
|
|
149
|
-
/** Figma file ID (the `:file_key` segment in the URL). */
|
|
150
|
-
fileId?: string;
|
|
151
|
-
/** GitHub repo (owner/name) used by the Tokens Studio plugin sync. */
|
|
152
|
-
githubRepo?: string;
|
|
153
|
-
/** GitHub branch the plugin pushes to. Defaults to `main`. */
|
|
154
|
-
githubBranch?: string;
|
|
155
|
-
}
|
|
156
|
-
export interface PenpotProviderConfig extends BaseProviderConfig {
|
|
157
|
-
provider: "penpot";
|
|
158
|
-
/** Penpot API URL — public cloud or self-hosted. */
|
|
159
|
-
apiUrl?: string;
|
|
160
|
-
/** Penpot personal access token. */
|
|
161
|
-
token?: string;
|
|
162
|
-
/** Penpot file ID. */
|
|
163
|
-
fileId?: string;
|
|
164
|
-
/** Penpot team / workspace ID. */
|
|
165
|
-
teamId?: string;
|
|
166
|
-
}
|
|
167
143
|
export interface GitOnlyProviderConfig extends BaseProviderConfig {
|
|
168
144
|
provider: "git-only";
|
|
169
145
|
}
|
|
@@ -187,5 +163,5 @@ export interface DesignMdProviderConfig extends BaseProviderConfig {
|
|
|
187
163
|
*/
|
|
188
164
|
description?: string;
|
|
189
165
|
}
|
|
190
|
-
export type DesignSyncConfig =
|
|
166
|
+
export type DesignSyncConfig = GitOnlyProviderConfig | MemoryProviderConfig | DesignMdProviderConfig;
|
|
191
167
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nebutra/design-sync",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Provider-agnostic design-tool sync.
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "Provider-agnostic design-tool sync. git-only | design-md | memory.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
@@ -10,19 +10,15 @@
|
|
|
10
10
|
"status": "foundation",
|
|
11
11
|
"productionReady": false,
|
|
12
12
|
"requires": [
|
|
13
|
-
"
|
|
14
|
-
"or PENPOT_API_URL + PENPOT_TOKEN for Penpot (self-hostable, China-friendly)",
|
|
15
|
-
"or nothing — git-only is the zero-config default"
|
|
13
|
+
"nothing — git-only is the zero-config default"
|
|
16
14
|
],
|
|
17
15
|
"gaps": [
|
|
18
|
-
"Figma push (Variables REST API) is dry-run; manual enable required",
|
|
19
|
-
"Penpot adapter implements healthcheck + pull; push is dry-run scaffolding",
|
|
20
16
|
"No real-time webhook listeners (manual pull/push only)",
|
|
21
17
|
"design-md provider depends on @google/design.md (alpha v0.x); pinned exactly — a 0.x bump may change linter rules and require re-validating the serializer output"
|
|
22
18
|
],
|
|
23
19
|
"featureId": "design-sync",
|
|
24
20
|
"category": "design",
|
|
25
|
-
"summary": "Provider-agnostic design-tool sync (
|
|
21
|
+
"summary": "Provider-agnostic design-tool sync (git-only | design-md)"
|
|
26
22
|
},
|
|
27
23
|
"main": "./dist/index.js",
|
|
28
24
|
"types": "./dist/index.d.ts",
|
|
@@ -39,14 +35,6 @@
|
|
|
39
35
|
"types": "./dist/serialize/from-design-md.d.ts",
|
|
40
36
|
"import": "./dist/serialize/from-design-md.js"
|
|
41
37
|
},
|
|
42
|
-
"./figma": {
|
|
43
|
-
"types": "./dist/providers/figma.d.ts",
|
|
44
|
-
"import": "./dist/providers/figma.js"
|
|
45
|
-
},
|
|
46
|
-
"./penpot": {
|
|
47
|
-
"types": "./dist/providers/penpot.d.ts",
|
|
48
|
-
"import": "./dist/providers/penpot.js"
|
|
49
|
-
},
|
|
50
38
|
"./git-only": {
|
|
51
39
|
"types": "./dist/providers/git-only.d.ts",
|
|
52
40
|
"import": "./dist/providers/git-only.js"
|
|
@@ -74,8 +62,8 @@
|
|
|
74
62
|
"dependencies": {
|
|
75
63
|
"@google/design.md": "0.2.0",
|
|
76
64
|
"zod": "^4.3.6",
|
|
77
|
-
"@nebutra/logger": "
|
|
78
|
-
"@nebutra/tokens": "
|
|
65
|
+
"@nebutra/logger": "3.0.0",
|
|
66
|
+
"@nebutra/tokens": "3.0.0"
|
|
79
67
|
},
|
|
80
68
|
"devDependencies": {
|
|
81
69
|
"@types/node": "^25.9.1",
|
package/src/cli/index.ts
CHANGED
|
@@ -97,12 +97,12 @@ Commands:
|
|
|
97
97
|
detect Print the resolved provider + env diagnostics
|
|
98
98
|
healthcheck Run the provider's healthcheck()
|
|
99
99
|
pull Pull design-tool → repo (DTCG JSON)
|
|
100
|
-
push Push repo → design-tool (defaults to dry-run on
|
|
100
|
+
push Push repo → design-tool (defaults to dry-run on design-md)
|
|
101
101
|
brand Pull tokens then compile a Brand Package (Create Center)
|
|
102
102
|
help Show this message
|
|
103
103
|
|
|
104
104
|
Options:
|
|
105
|
-
--provider <X> Override provider (
|
|
105
|
+
--provider <X> Override provider (git-only|memory|design-md)
|
|
106
106
|
--themes a,b Restrict to specific token sets
|
|
107
107
|
--id <id> Brand package id (for brand command)
|
|
108
108
|
--name <name> Brand package display name
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import figmaPluginConfig from "./tokens-studio.config.json";
|
|
2
|
-
export declare const FIGMA_TOKENS_STUDIO_CONFIG: {
|
|
3
|
-
$schema: string;
|
|
4
|
-
name: string;
|
|
5
|
-
version: string;
|
|
6
|
-
description: string;
|
|
7
|
-
syncProvider: string;
|
|
8
|
-
git: {
|
|
9
|
-
repository: string;
|
|
10
|
-
defaultBranch: string;
|
|
11
|
-
syncBranchPrefix: string;
|
|
12
|
-
tokenPath: string;
|
|
13
|
-
themesPath: string;
|
|
14
|
-
metadataPath: string;
|
|
15
|
-
};
|
|
16
|
-
format: {
|
|
17
|
-
spec: string;
|
|
18
|
-
indent: number;
|
|
19
|
-
trailingNewline: boolean;
|
|
20
|
-
useUiCompatibleTypes: boolean;
|
|
21
|
-
};
|
|
22
|
-
storage: {
|
|
23
|
-
multiFile: boolean;
|
|
24
|
-
tokenSetSeparator: string;
|
|
25
|
-
};
|
|
26
|
-
permissions: {
|
|
27
|
-
allowDestructiveSync: boolean;
|
|
28
|
-
requirePullRequest: boolean;
|
|
29
|
-
protectedSets: string[];
|
|
30
|
-
};
|
|
31
|
-
ui: {
|
|
32
|
-
language: string;
|
|
33
|
-
showThemeMatrix: boolean;
|
|
34
|
-
};
|
|
35
|
-
};
|
|
36
|
-
export type FigmaTokensStudioConfig = typeof figmaPluginConfig;
|
|
37
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/figma-config/index.ts"],"names":[],"mappings":"AAWA,OAAO,iBAAiB,MAAM,6BAA6B,CAAuB;AAElF,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAoB,CAAC;AAE5D,MAAM,MAAM,uBAAuB,GAAG,OAAO,iBAAiB,CAAC"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
// =============================================================================
|
|
2
|
-
// Tokens Studio config — canonical copy owned by @nebutra/design-sync
|
|
3
|
-
// =============================================================================
|
|
4
|
-
// The original `.tokens-studio/config.json` lives at the repo root because the
|
|
5
|
-
// Tokens Studio plugin pins its config-discovery path. We keep an in-package
|
|
6
|
-
// canonical copy here so the figma provider can self-document and so a sync
|
|
7
|
-
// CLI can scaffold the file when initialising a fresh repo.
|
|
8
|
-
//
|
|
9
|
-
// If the plugin's config schema changes, update both files in lock-step.
|
|
10
|
-
// =============================================================================
|
|
11
|
-
import figmaPluginConfig from "./tokens-studio.config.json" with { type: "json" };
|
|
12
|
-
export const FIGMA_TOKENS_STUDIO_CONFIG = figmaPluginConfig;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://raw.githubusercontent.com/tokens-studio/figma-plugin/main/schemas/tokens-studio-config.schema.json",
|
|
3
|
-
"name": "Nebutra Design Tokens",
|
|
4
|
-
"version": "0.1.0",
|
|
5
|
-
"description": "Bidirectional sync between Tokens Studio for Figma and packages/design-tokens (W3C DTCG). Owned by @nebutra/design-sync's figma provider.",
|
|
6
|
-
"syncProvider": "github",
|
|
7
|
-
"git": {
|
|
8
|
-
"repository": "Nebutra/Nebutra-Sailor",
|
|
9
|
-
"defaultBranch": "main",
|
|
10
|
-
"syncBranchPrefix": "tokens-studio/",
|
|
11
|
-
"tokenPath": "packages/design/design-tokens/tokens",
|
|
12
|
-
"themesPath": ".tokens-studio/themes.json",
|
|
13
|
-
"metadataPath": ".tokens-studio/metadata.json"
|
|
14
|
-
},
|
|
15
|
-
"format": {
|
|
16
|
-
"spec": "dtcg",
|
|
17
|
-
"indent": 2,
|
|
18
|
-
"trailingNewline": true,
|
|
19
|
-
"useUiCompatibleTypes": true
|
|
20
|
-
},
|
|
21
|
-
"storage": {
|
|
22
|
-
"multiFile": true,
|
|
23
|
-
"tokenSetSeparator": "/"
|
|
24
|
-
},
|
|
25
|
-
"permissions": {
|
|
26
|
-
"allowDestructiveSync": false,
|
|
27
|
-
"requirePullRequest": true,
|
|
28
|
-
"protectedSets": ["core", "semantic"]
|
|
29
|
-
},
|
|
30
|
-
"ui": {
|
|
31
|
-
"language": "en",
|
|
32
|
-
"showThemeMatrix": true
|
|
33
|
-
}
|
|
34
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { DesignSyncProvider, FigmaProviderConfig, HealthStatus, PullOptions, PullResult, PushOptions, PushResult } from "../types";
|
|
2
|
-
export declare class FigmaProvider implements DesignSyncProvider {
|
|
3
|
-
readonly name: "figma";
|
|
4
|
-
private readonly tokensDir;
|
|
5
|
-
private readonly tokensStudioDir;
|
|
6
|
-
private readonly personalAccessToken;
|
|
7
|
-
private readonly fileId;
|
|
8
|
-
private readonly githubRepo;
|
|
9
|
-
private readonly githubBranch;
|
|
10
|
-
constructor(config: FigmaProviderConfig);
|
|
11
|
-
pull(options?: PullOptions): Promise<PullResult>;
|
|
12
|
-
push(options?: PushOptions): Promise<PushResult>;
|
|
13
|
-
healthcheck(): Promise<HealthStatus>;
|
|
14
|
-
/**
|
|
15
|
-
* Assert that `.tokens-studio/{config,metadata,themes}.json` exist + parse.
|
|
16
|
-
* The plugin refuses to load the design system if any of these are missing
|
|
17
|
-
* or malformed; surfacing the failure here prevents silent drift.
|
|
18
|
-
*/
|
|
19
|
-
private assertTokensStudioMetadata;
|
|
20
|
-
}
|
|
21
|
-
//# sourceMappingURL=figma.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"figma.d.ts","sourceRoot":"","sources":["../../src/providers/figma.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,EACZ,WAAW,EACX,UAAU,EACV,WAAW,EACX,UAAU,EACX,MAAM,UAAU,CAAC;AAyBlB,qBAAa,aAAc,YAAW,kBAAkB;IACtD,QAAQ,CAAC,IAAI,EAAG,OAAO,CAAU;IAEjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;gBAE1B,MAAM,EAAE,mBAAmB;IAgBjC,IAAI,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IAiBpD,IAAI,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IAiDpD,WAAW,IAAI,OAAO,CAAC,YAAY,CAAC;IAwC1C;;;;OAIG;YACW,0BAA0B;CA+BzC"}
|
package/dist/providers/figma.js
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { readFile, stat } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { logger } from "@nebutra/logger";
|
|
4
|
-
import { defaultTokensDir, defaultTokensStudioDir, readTokenSets, validateDtcgTree } from "../io";
|
|
5
|
-
// =============================================================================
|
|
6
|
-
// Figma Provider — Figma + Tokens Studio
|
|
7
|
-
// =============================================================================
|
|
8
|
-
// Architecture (matches the legacy `.tokens-studio/config.json` setup):
|
|
9
|
-
//
|
|
10
|
-
// Figma (Tokens Studio plugin) ──► GitHub branch ──► repo (DTCG JSON)
|
|
11
|
-
// ▲ │
|
|
12
|
-
// └─── pull from main ──────────┘
|
|
13
|
-
//
|
|
14
|
-
// The plugin owns the git transport. This provider:
|
|
15
|
-
// - validates that the Tokens Studio config + DTCG tree are well-formed
|
|
16
|
-
// - "pull" reads the DTCG files the plugin already wrote to git
|
|
17
|
-
// - "push" is a dry-run scaffold for the Figma Variables REST API
|
|
18
|
-
// (PATCH /v1/files/:file_key/variables) — the user must opt in by
|
|
19
|
-
// providing FIGMA_PERSONAL_ACCESS_TOKEN + FIGMA_FILE_ID and removing
|
|
20
|
-
// the early-exit guard.
|
|
21
|
-
//
|
|
22
|
-
// All Tokens Studio metadata that previously lived in `.tokens-studio/` is
|
|
23
|
-
// still consumed from disk so the existing designer onboarding does not break.
|
|
24
|
-
// =============================================================================
|
|
25
|
-
const FIGMA_API_ROOT = "https://api.figma.com";
|
|
26
|
-
export class FigmaProvider {
|
|
27
|
-
name = "figma";
|
|
28
|
-
tokensDir;
|
|
29
|
-
tokensStudioDir;
|
|
30
|
-
personalAccessToken;
|
|
31
|
-
fileId;
|
|
32
|
-
githubRepo;
|
|
33
|
-
githubBranch;
|
|
34
|
-
constructor(config) {
|
|
35
|
-
this.tokensDir = config.tokensDir ?? defaultTokensDir();
|
|
36
|
-
this.tokensStudioDir = config.tokensStudioDir ?? defaultTokensStudioDir();
|
|
37
|
-
this.personalAccessToken =
|
|
38
|
-
config.personalAccessToken ?? process.env.FIGMA_PERSONAL_ACCESS_TOKEN ?? undefined;
|
|
39
|
-
this.fileId = config.fileId ?? process.env.FIGMA_FILE_ID ?? undefined;
|
|
40
|
-
this.githubRepo = config.githubRepo ?? process.env.FIGMA_GITHUB_REPO ?? undefined;
|
|
41
|
-
this.githubBranch = config.githubBranch ?? process.env.FIGMA_GITHUB_BRANCH ?? "main";
|
|
42
|
-
logger.info("[design-sync:figma] Provider initialised", {
|
|
43
|
-
tokensDir: this.tokensDir,
|
|
44
|
-
hasToken: Boolean(this.personalAccessToken),
|
|
45
|
-
hasFileId: Boolean(this.fileId),
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
async pull(options = {}) {
|
|
49
|
-
// Tokens Studio plugin already wrote DTCG files to git on `pull`.
|
|
50
|
-
// We just re-read them from disk and (optionally) validate Tokens
|
|
51
|
-
// Studio metadata so the result mirrors what the plugin would emit.
|
|
52
|
-
const sets = await readTokenSets(this.tokensDir);
|
|
53
|
-
const filtered = filterSets(sets, options.themes);
|
|
54
|
-
await this.assertTokensStudioMetadata();
|
|
55
|
-
return {
|
|
56
|
-
sets: filtered,
|
|
57
|
-
written: false,
|
|
58
|
-
provider: "figma",
|
|
59
|
-
pulledAt: new Date().toISOString(),
|
|
60
|
-
summary: `figma: read ${filtered.length} DTCG token set(s) (Tokens Studio plugin owns the git transport)`,
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
async push(options = {}) {
|
|
64
|
-
// Pre-flight: validate DTCG before attempting any remote call.
|
|
65
|
-
const all = await readTokenSets(this.tokensDir);
|
|
66
|
-
const sets = filterSets(all, options.themes);
|
|
67
|
-
for (const set of sets) {
|
|
68
|
-
const errors = validateDtcgTree(set.tokens);
|
|
69
|
-
if (errors.length > 0) {
|
|
70
|
-
throw new Error(`[design-sync:figma] DTCG validation failed for ${set.relativePath}:\n - ${errors.join("\n - ")}`);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
const credsReady = Boolean(this.personalAccessToken && this.fileId);
|
|
74
|
-
const explicitDryRun = options.dryRun ?? false;
|
|
75
|
-
const dryRun = explicitDryRun || !credsReady;
|
|
76
|
-
if (dryRun) {
|
|
77
|
-
logger.warn("[design-sync:figma] push skipped (dry-run scaffold)", {
|
|
78
|
-
reason: credsReady ? "explicit dryRun" : "missing credentials",
|
|
79
|
-
sets: sets.length,
|
|
80
|
-
});
|
|
81
|
-
return {
|
|
82
|
-
pushed: false,
|
|
83
|
-
sets: sets.map((s) => s.relativePath),
|
|
84
|
-
provider: "figma",
|
|
85
|
-
pushedAt: new Date().toISOString(),
|
|
86
|
-
summary: credsReady
|
|
87
|
-
? `figma: dry-run — would PATCH ${sets.length} set(s) to ${FIGMA_API_ROOT}/v1/files/${this.fileId}/variables`
|
|
88
|
-
: "figma: dry-run — FIGMA_PERSONAL_ACCESS_TOKEN or FIGMA_FILE_ID not set",
|
|
89
|
-
dryRun: true,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
// Real push placeholder — kept guarded until the user opts in.
|
|
93
|
-
// The integration will call the Figma Variables REST API:
|
|
94
|
-
// PATCH /v1/files/:file_key/variables
|
|
95
|
-
// X-Figma-Token: <personalAccessToken>
|
|
96
|
-
// See https://www.figma.com/developers/api#variables
|
|
97
|
-
throw new Error("[design-sync:figma] live push to Figma Variables REST API is not yet implemented. " +
|
|
98
|
-
"Use { dryRun: true } or unset FIGMA_PERSONAL_ACCESS_TOKEN until the integration is wired up. " +
|
|
99
|
-
"See packages/design/design-sync/DESIGN.md for the rollout plan.");
|
|
100
|
-
}
|
|
101
|
-
async healthcheck() {
|
|
102
|
-
const detected = [];
|
|
103
|
-
const missing = [];
|
|
104
|
-
if (this.personalAccessToken)
|
|
105
|
-
detected.push("FIGMA_PERSONAL_ACCESS_TOKEN");
|
|
106
|
-
else
|
|
107
|
-
missing.push("FIGMA_PERSONAL_ACCESS_TOKEN");
|
|
108
|
-
if (this.fileId)
|
|
109
|
-
detected.push("FIGMA_FILE_ID");
|
|
110
|
-
else
|
|
111
|
-
missing.push("FIGMA_FILE_ID");
|
|
112
|
-
if (this.githubRepo)
|
|
113
|
-
detected.push("FIGMA_GITHUB_REPO");
|
|
114
|
-
if (this.githubBranch)
|
|
115
|
-
detected.push(`FIGMA_GITHUB_BRANCH=${this.githubBranch}`);
|
|
116
|
-
let tokensStudioOk = true;
|
|
117
|
-
let tokensStudioMessage = "";
|
|
118
|
-
try {
|
|
119
|
-
await this.assertTokensStudioMetadata();
|
|
120
|
-
}
|
|
121
|
-
catch (error) {
|
|
122
|
-
tokensStudioOk = false;
|
|
123
|
-
tokensStudioMessage = error.message;
|
|
124
|
-
missing.push("tokens-studio metadata");
|
|
125
|
-
}
|
|
126
|
-
const ok = missing.length === 0 && tokensStudioOk;
|
|
127
|
-
return {
|
|
128
|
-
ok,
|
|
129
|
-
provider: "figma",
|
|
130
|
-
message: ok
|
|
131
|
-
? "figma: credentials present + Tokens Studio metadata valid"
|
|
132
|
-
: `figma: not ready — ${[
|
|
133
|
-
missing.length > 0 ? `missing ${missing.join(", ")}` : "",
|
|
134
|
-
tokensStudioMessage,
|
|
135
|
-
]
|
|
136
|
-
.filter(Boolean)
|
|
137
|
-
.join("; ")}`,
|
|
138
|
-
detectedEnv: detected,
|
|
139
|
-
missingEnv: missing,
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Assert that `.tokens-studio/{config,metadata,themes}.json` exist + parse.
|
|
144
|
-
* The plugin refuses to load the design system if any of these are missing
|
|
145
|
-
* or malformed; surfacing the failure here prevents silent drift.
|
|
146
|
-
*/
|
|
147
|
-
async assertTokensStudioMetadata() {
|
|
148
|
-
const required = ["config.json", "metadata.json", "themes.json"];
|
|
149
|
-
const errors = [];
|
|
150
|
-
try {
|
|
151
|
-
const info = await stat(this.tokensStudioDir);
|
|
152
|
-
if (!info.isDirectory()) {
|
|
153
|
-
throw new Error(`${this.tokensStudioDir} is not a directory`);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
catch (error) {
|
|
157
|
-
throw new Error(`[design-sync:figma] Tokens Studio metadata directory missing at ${this.tokensStudioDir}: ${error.message}`);
|
|
158
|
-
}
|
|
159
|
-
for (const file of required) {
|
|
160
|
-
const path = join(this.tokensStudioDir, file);
|
|
161
|
-
try {
|
|
162
|
-
const raw = await readFile(path, "utf8");
|
|
163
|
-
JSON.parse(raw);
|
|
164
|
-
}
|
|
165
|
-
catch (error) {
|
|
166
|
-
errors.push(`${path}: ${error.message}`);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
if (errors.length > 0) {
|
|
170
|
-
throw new Error(`[design-sync:figma] Tokens Studio metadata invalid:\n - ${errors.join("\n - ")}`);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
function filterSets(sets, themes) {
|
|
175
|
-
if (!themes || themes.length === 0)
|
|
176
|
-
return sets;
|
|
177
|
-
const wanted = new Set(themes);
|
|
178
|
-
return sets.filter((s) => wanted.has(s.name) || wanted.has(s.relativePath));
|
|
179
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { DesignSyncProvider, HealthStatus, PenpotProviderConfig, PullOptions, PullResult, PushOptions, PushResult } from "../types";
|
|
2
|
-
export declare class PenpotProvider implements DesignSyncProvider {
|
|
3
|
-
readonly name: "penpot";
|
|
4
|
-
private readonly tokensDir;
|
|
5
|
-
private readonly tokensStudioDir;
|
|
6
|
-
private readonly apiUrl;
|
|
7
|
-
private readonly token;
|
|
8
|
-
private readonly fileId;
|
|
9
|
-
private readonly teamId;
|
|
10
|
-
constructor(config: PenpotProviderConfig);
|
|
11
|
-
pull(options?: PullOptions): Promise<PullResult>;
|
|
12
|
-
push(options?: PushOptions): Promise<PushResult>;
|
|
13
|
-
healthcheck(): Promise<HealthStatus>;
|
|
14
|
-
/**
|
|
15
|
-
* Reserved for future use — exposes the configured tokens-studio directory
|
|
16
|
-
* so consumers (e.g. CI workflows) can attach Penpot-side metadata mirrors.
|
|
17
|
-
*/
|
|
18
|
-
getTokensStudioDir(): string;
|
|
19
|
-
private credsReady;
|
|
20
|
-
}
|
|
21
|
-
//# sourceMappingURL=penpot.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"penpot.d.ts","sourceRoot":"","sources":["../../src/providers/penpot.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,oBAAoB,EACpB,WAAW,EACX,UAAU,EACV,WAAW,EACX,UAAU,EACX,MAAM,UAAU,CAAC;AAmBlB,qBAAa,cAAe,YAAW,kBAAkB;IACvD,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAU;IAElC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;gBAEhC,MAAM,EAAE,oBAAoB;IAelC,IAAI,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IA0BpD,IAAI,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IA8CpD,WAAW,IAAI,OAAO,CAAC,YAAY,CAAC;IAyB1C;;;OAGG;IACH,kBAAkB,IAAI,MAAM;IAI5B,OAAO,CAAC,UAAU;CAGnB"}
|
package/dist/providers/penpot.js
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { logger } from "@nebutra/logger";
|
|
2
|
-
import { defaultTokensDir, defaultTokensStudioDir, readTokenSets, validateDtcgTree } from "../io";
|
|
3
|
-
// =============================================================================
|
|
4
|
-
// Penpot Provider — China-friendly + self-hostable Figma alternative
|
|
5
|
-
// =============================================================================
|
|
6
|
-
// Penpot speaks DTCG natively (one of its differentiators) and exposes a
|
|
7
|
-
// public REST API. The provider is wired with full surface area so customers
|
|
8
|
-
// can swap providers without changing application code, but real network
|
|
9
|
-
// calls stay behind a `dryRun || !credsReady` guard until the operator
|
|
10
|
-
// explicitly provisions PENPOT_TOKEN.
|
|
11
|
-
//
|
|
12
|
-
// Why this matters:
|
|
13
|
-
// - Penpot can be self-hosted, sidesteps the Figma block in mainland China.
|
|
14
|
-
// - DTCG-native means we do not need a translation step like with Figma.
|
|
15
|
-
// - Tokens Studio plugin also targets Penpot — designers keep the same UX.
|
|
16
|
-
// =============================================================================
|
|
17
|
-
const DEFAULT_PENPOT_API = "https://design.penpot.app/api";
|
|
18
|
-
export class PenpotProvider {
|
|
19
|
-
name = "penpot";
|
|
20
|
-
tokensDir;
|
|
21
|
-
tokensStudioDir;
|
|
22
|
-
apiUrl;
|
|
23
|
-
token;
|
|
24
|
-
fileId;
|
|
25
|
-
teamId;
|
|
26
|
-
constructor(config) {
|
|
27
|
-
this.tokensDir = config.tokensDir ?? defaultTokensDir();
|
|
28
|
-
this.tokensStudioDir = config.tokensStudioDir ?? defaultTokensStudioDir();
|
|
29
|
-
this.apiUrl = config.apiUrl ?? process.env.PENPOT_API_URL ?? DEFAULT_PENPOT_API;
|
|
30
|
-
this.token = config.token ?? process.env.PENPOT_TOKEN ?? undefined;
|
|
31
|
-
this.fileId = config.fileId ?? process.env.PENPOT_FILE_ID ?? undefined;
|
|
32
|
-
this.teamId = config.teamId ?? process.env.PENPOT_TEAM_ID ?? undefined;
|
|
33
|
-
logger.info("[design-sync:penpot] Provider initialised", {
|
|
34
|
-
apiUrl: this.apiUrl,
|
|
35
|
-
hasToken: Boolean(this.token),
|
|
36
|
-
hasFileId: Boolean(this.fileId),
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
async pull(options = {}) {
|
|
40
|
-
if (!this.credsReady()) {
|
|
41
|
-
// No credentials → fall through to local DTCG, mirroring git-only.
|
|
42
|
-
const sets = await readTokenSets(this.tokensDir);
|
|
43
|
-
const filtered = filterSets(sets, options.themes);
|
|
44
|
-
logger.warn("[design-sync:penpot] pull falling back to local DTCG (no PENPOT_TOKEN)");
|
|
45
|
-
return {
|
|
46
|
-
sets: filtered,
|
|
47
|
-
written: false,
|
|
48
|
-
provider: "penpot",
|
|
49
|
-
pulledAt: new Date().toISOString(),
|
|
50
|
-
summary: `penpot: PENPOT_TOKEN missing — returned ${filtered.length} local DTCG set(s)`,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
// Real pull placeholder — when wired up, this calls:
|
|
54
|
-
// GET {apiUrl}/rpc/command/get-file?id={fileId}
|
|
55
|
-
// Authorization: Token {token}
|
|
56
|
-
// and projects the design-tokens collection back into DTCG sets.
|
|
57
|
-
// See https://design.penpot.app/api/docs for the full RPC catalogue.
|
|
58
|
-
throw new Error("[design-sync:penpot] live pull is not yet implemented. " +
|
|
59
|
-
"Unset PENPOT_TOKEN to fall back to local DTCG, or open packages/design/design-sync/DESIGN.md for the rollout plan.");
|
|
60
|
-
}
|
|
61
|
-
async push(options = {}) {
|
|
62
|
-
const all = await readTokenSets(this.tokensDir);
|
|
63
|
-
const sets = filterSets(all, options.themes);
|
|
64
|
-
for (const set of sets) {
|
|
65
|
-
const errors = validateDtcgTree(set.tokens);
|
|
66
|
-
if (errors.length > 0) {
|
|
67
|
-
throw new Error(`[design-sync:penpot] DTCG validation failed for ${set.relativePath}:\n - ${errors.join("\n - ")}`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
const credsReady = this.credsReady();
|
|
71
|
-
const explicitDryRun = options.dryRun ?? false;
|
|
72
|
-
const dryRun = explicitDryRun || !credsReady;
|
|
73
|
-
if (dryRun) {
|
|
74
|
-
logger.warn("[design-sync:penpot] push skipped (dry-run scaffold)", {
|
|
75
|
-
reason: credsReady ? "explicit dryRun" : "missing credentials",
|
|
76
|
-
sets: sets.length,
|
|
77
|
-
});
|
|
78
|
-
return {
|
|
79
|
-
pushed: false,
|
|
80
|
-
sets: sets.map((s) => s.relativePath),
|
|
81
|
-
provider: "penpot",
|
|
82
|
-
pushedAt: new Date().toISOString(),
|
|
83
|
-
summary: credsReady
|
|
84
|
-
? `penpot: dry-run — would POST ${sets.length} DTCG set(s) to ${this.apiUrl}/rpc/command/update-file-design-tokens`
|
|
85
|
-
: "penpot: dry-run — PENPOT_TOKEN or PENPOT_FILE_ID missing",
|
|
86
|
-
dryRun: true,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
// Real push placeholder — when wired up, this calls:
|
|
90
|
-
// POST {apiUrl}/rpc/command/update-file-design-tokens
|
|
91
|
-
// Authorization: Token {token}
|
|
92
|
-
// body: { fileId, tokens: dtcgTree }
|
|
93
|
-
throw new Error("[design-sync:penpot] live push is not yet implemented. " +
|
|
94
|
-
"Use { dryRun: true } until the Penpot RPC integration is wired up.");
|
|
95
|
-
}
|
|
96
|
-
async healthcheck() {
|
|
97
|
-
const detected = [];
|
|
98
|
-
const missing = [];
|
|
99
|
-
if (this.token)
|
|
100
|
-
detected.push("PENPOT_TOKEN");
|
|
101
|
-
else
|
|
102
|
-
missing.push("PENPOT_TOKEN");
|
|
103
|
-
if (this.fileId)
|
|
104
|
-
detected.push("PENPOT_FILE_ID");
|
|
105
|
-
else
|
|
106
|
-
missing.push("PENPOT_FILE_ID");
|
|
107
|
-
if (this.teamId)
|
|
108
|
-
detected.push("PENPOT_TEAM_ID");
|
|
109
|
-
detected.push(`PENPOT_API_URL=${this.apiUrl}`);
|
|
110
|
-
const ok = missing.length === 0;
|
|
111
|
-
return {
|
|
112
|
-
ok,
|
|
113
|
-
provider: "penpot",
|
|
114
|
-
message: ok
|
|
115
|
-
? `penpot: credentials present (api=${this.apiUrl})`
|
|
116
|
-
: `penpot: not ready — missing ${missing.join(", ")}`,
|
|
117
|
-
detectedEnv: detected,
|
|
118
|
-
missingEnv: missing,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Reserved for future use — exposes the configured tokens-studio directory
|
|
123
|
-
* so consumers (e.g. CI workflows) can attach Penpot-side metadata mirrors.
|
|
124
|
-
*/
|
|
125
|
-
getTokensStudioDir() {
|
|
126
|
-
return this.tokensStudioDir;
|
|
127
|
-
}
|
|
128
|
-
credsReady() {
|
|
129
|
-
return Boolean(this.token && this.fileId);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
function filterSets(sets, themes) {
|
|
133
|
-
if (!themes || themes.length === 0)
|
|
134
|
-
return sets;
|
|
135
|
-
const wanted = new Set(themes);
|
|
136
|
-
return sets.filter((s) => wanted.has(s.name) || wanted.has(s.relativePath));
|
|
137
|
-
}
|