@sxl-studio/storybook-addon 1.0.16 → 1.0.18
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 +6 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +7 -0
- package/dist/manager.js +6 -0
- package/dist/preset.js +163 -1
- package/package.json +5 -3
- package/preset.js +1 -0
package/README.md
CHANGED
|
@@ -32,7 +32,12 @@ export default {
|
|
|
32
32
|
|
|
33
33
|
### 2. Import the registry
|
|
34
34
|
|
|
35
|
-
The SXL Studio Figma plugin generates a `sxl-codeconnect.json` (or `diff-code-connect.<fileKey>.json`) file that maps Figma components to your codebase. Import it in your Storybook preview config
|
|
35
|
+
The SXL Studio Figma plugin generates a `sxl-codeconnect.json` (or `diff-code-connect.<fileKey>.json`) file that maps Figma components to your codebase. Import it in your Storybook preview config.
|
|
36
|
+
|
|
37
|
+
**Preset (automatic):** when the addon preset is loaded (default if the package is listed under `addons`), it:
|
|
38
|
+
|
|
39
|
+
- Serves the sibling folder `tokens/tokens` (if present next to the Storybook app or two levels above `.storybook`) at **`/sxl-tokens/…`**, so composition JSON paths from the registry resolve in dev and via `staticDirs` in `storybook build`.
|
|
40
|
+
- If `diff-code-connect.SXL-Components.json` is **missing** but another `diff-code-connect.*.json` exists in that folder, Vite gets a **`resolve.alias`** from the legacy path to the first matching file (sorted by name). You can keep a stable import path in preview while the plugin emits `diff-code-connect.<figmaFileKey>.json`.
|
|
36
41
|
|
|
37
42
|
**Option A** — using `sxl-codeconnect.json` (exported from plugin):
|
|
38
43
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
declare const ADDON_ID: "sxl-studio";
|
|
2
2
|
declare const PANEL_ID: "sxl-studio/panel";
|
|
3
3
|
declare const PARAM_KEY: "sxl";
|
|
4
|
+
/** Same-origin path where the preset may serve `tokens/tokens` (composition JSON). */
|
|
5
|
+
declare const SXL_TOKENS_URL_PREFIX: "/sxl-tokens";
|
|
4
6
|
|
|
5
7
|
/** Token row: green "True" or gray "False" (from `tokensReady` in registry). */
|
|
6
8
|
type SxlTokensBool = "true" | "false";
|
|
@@ -141,4 +143,4 @@ declare function loadCompositionJsonText(path: string | undefined, legacyInline:
|
|
|
141
143
|
error: string;
|
|
142
144
|
}>;
|
|
143
145
|
|
|
144
|
-
export { ADDON_ID, PANEL_ID, PARAM_KEY, type SxlComponentApiProp, type SxlFileBinding, type SxlReadiness, type SxlRegistry, type SxlRegistryEntry, type SxlRegistryMeta, type SxlStoryParameters, type SxlTokenStatus, type SxlTokensBool, fromDiffCodeConnect, loadCompositionJsonText, normalizeCompositionPath };
|
|
146
|
+
export { ADDON_ID, PANEL_ID, PARAM_KEY, SXL_TOKENS_URL_PREFIX, type SxlComponentApiProp, type SxlFileBinding, type SxlReadiness, type SxlRegistry, type SxlRegistryEntry, type SxlRegistryMeta, type SxlStoryParameters, type SxlTokenStatus, type SxlTokensBool, fromDiffCodeConnect, loadCompositionJsonText, normalizeCompositionPath };
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
var ADDON_ID = "sxl-studio";
|
|
3
3
|
var PANEL_ID = `${ADDON_ID}/panel`;
|
|
4
4
|
var PARAM_KEY = "sxl";
|
|
5
|
+
var SXL_TOKENS_URL_PREFIX = "/sxl-tokens";
|
|
5
6
|
|
|
6
7
|
// src/convert.ts
|
|
7
8
|
function fromDiffCodeConnect(data) {
|
|
@@ -269,6 +270,11 @@ async function loadCompositionJsonText(path, legacyInline, params) {
|
|
|
269
270
|
};
|
|
270
271
|
}
|
|
271
272
|
}
|
|
273
|
+
const sxlPresetUrls = candidateRelativePaths(p).map((rel) => joinFetchUrl(SXL_TOKENS_URL_PREFIX, rel));
|
|
274
|
+
const sxlPresetHit = await fetchFirstOk(sxlPresetUrls);
|
|
275
|
+
if (sxlPresetHit) {
|
|
276
|
+
return { text: sxlPresetHit.text, source: "fetch" };
|
|
277
|
+
}
|
|
272
278
|
const repoUrl = extractRepositoryUrl(params);
|
|
273
279
|
if (repoUrl) {
|
|
274
280
|
const gitlabCandidates = buildGitlabRawCandidates(repoUrl, p);
|
|
@@ -290,6 +296,7 @@ export {
|
|
|
290
296
|
ADDON_ID,
|
|
291
297
|
PANEL_ID,
|
|
292
298
|
PARAM_KEY,
|
|
299
|
+
SXL_TOKENS_URL_PREFIX,
|
|
293
300
|
fromDiffCodeConnect,
|
|
294
301
|
loadCompositionJsonText,
|
|
295
302
|
normalizeCompositionPath
|
package/dist/manager.js
CHANGED
|
@@ -6,6 +6,7 @@ import { addons, types } from "@storybook/manager-api";
|
|
|
6
6
|
var ADDON_ID = "sxl-studio";
|
|
7
7
|
var PANEL_ID = `${ADDON_ID}/panel`;
|
|
8
8
|
var PARAM_KEY = "sxl";
|
|
9
|
+
var SXL_TOKENS_URL_PREFIX = "/sxl-tokens";
|
|
9
10
|
|
|
10
11
|
// src/components/SxlPanel.tsx
|
|
11
12
|
import React2, { useCallback as useCallback3, useEffect, useState } from "react";
|
|
@@ -277,6 +278,11 @@ async function loadCompositionJsonText(path, legacyInline, params) {
|
|
|
277
278
|
};
|
|
278
279
|
}
|
|
279
280
|
}
|
|
281
|
+
const sxlPresetUrls = candidateRelativePaths(p).map((rel) => joinFetchUrl(SXL_TOKENS_URL_PREFIX, rel));
|
|
282
|
+
const sxlPresetHit = await fetchFirstOk(sxlPresetUrls);
|
|
283
|
+
if (sxlPresetHit) {
|
|
284
|
+
return { text: sxlPresetHit.text, source: "fetch" };
|
|
285
|
+
}
|
|
280
286
|
const repoUrl = extractRepositoryUrl(params);
|
|
281
287
|
if (repoUrl) {
|
|
282
288
|
const gitlabCandidates = buildGitlabRawCandidates(repoUrl, p);
|
package/dist/preset.js
CHANGED
|
@@ -1,10 +1,170 @@
|
|
|
1
1
|
// src/preset.ts
|
|
2
2
|
import { dirname, join } from "path";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
|
+
|
|
5
|
+
// src/sxlTokensServe.ts
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
|
|
9
|
+
// src/constants.ts
|
|
10
|
+
var ADDON_ID = "sxl-studio";
|
|
11
|
+
var PANEL_ID = `${ADDON_ID}/panel`;
|
|
12
|
+
var SXL_TOKENS_URL_PREFIX = "/sxl-tokens";
|
|
13
|
+
|
|
14
|
+
// src/sxlTokensServe.ts
|
|
15
|
+
var LEGACY_DIFF_CODE_CONNECT_BASENAME = "diff-code-connect.SXL-Components.json";
|
|
16
|
+
function resolveDiffCodeConnectAliasFallback(tokensRoot) {
|
|
17
|
+
const legacyPath = path.resolve(tokensRoot, LEGACY_DIFF_CODE_CONNECT_BASENAME);
|
|
18
|
+
if (fs.existsSync(legacyPath)) return null;
|
|
19
|
+
let names = [];
|
|
20
|
+
try {
|
|
21
|
+
names = fs.readdirSync(tokensRoot).filter((f) => f.startsWith("diff-code-connect.") && f.endsWith(".json"));
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
if (names.length === 0) return null;
|
|
26
|
+
names.sort();
|
|
27
|
+
const replacement = path.resolve(tokensRoot, names[0]);
|
|
28
|
+
return { legacyAbsolutePath: legacyPath, replacementAbsolutePath: replacement };
|
|
29
|
+
}
|
|
30
|
+
function resolveSxlTokensRootSync(baseDirs) {
|
|
31
|
+
const seen = /* @__PURE__ */ new Set();
|
|
32
|
+
for (const base of baseDirs) {
|
|
33
|
+
if (!base) continue;
|
|
34
|
+
const resolvedBase = path.resolve(base);
|
|
35
|
+
const candidates = [
|
|
36
|
+
path.join(resolvedBase, "tokens", "tokens"),
|
|
37
|
+
path.join(resolvedBase, "..", "tokens", "tokens"),
|
|
38
|
+
path.join(resolvedBase, "..", "..", "tokens", "tokens")
|
|
39
|
+
];
|
|
40
|
+
for (const c of candidates) {
|
|
41
|
+
try {
|
|
42
|
+
const rp = path.resolve(c);
|
|
43
|
+
if (seen.has(rp)) continue;
|
|
44
|
+
seen.add(rp);
|
|
45
|
+
if (fs.existsSync(rp) && fs.statSync(rp).isDirectory()) {
|
|
46
|
+
return rp;
|
|
47
|
+
}
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
function isPathInsideRoot(root, filePath) {
|
|
55
|
+
const r = path.resolve(root) + path.sep;
|
|
56
|
+
const f = path.resolve(filePath);
|
|
57
|
+
return f === path.resolve(root) || f.startsWith(r);
|
|
58
|
+
}
|
|
59
|
+
function createSxlTokensDevPlugin(tokensRoot) {
|
|
60
|
+
return {
|
|
61
|
+
name: "sxl-studio-tokens-static",
|
|
62
|
+
configureServer(server) {
|
|
63
|
+
server.middlewares.use((req, res, next) => {
|
|
64
|
+
const rawUrl = req.url ?? "";
|
|
65
|
+
if (!rawUrl.startsWith(`${SXL_TOKENS_URL_PREFIX}/`) && rawUrl !== `${SXL_TOKENS_URL_PREFIX}`) {
|
|
66
|
+
next();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const rel = decodeURIComponent(rawUrl.slice(SXL_TOKENS_URL_PREFIX.length).replace(/^\//, ""));
|
|
70
|
+
if (!rel || rel.includes("..")) {
|
|
71
|
+
res.statusCode = 400;
|
|
72
|
+
res.end();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const fp = path.join(tokensRoot, rel);
|
|
76
|
+
if (!isPathInsideRoot(tokensRoot, fp)) {
|
|
77
|
+
res.statusCode = 403;
|
|
78
|
+
res.end();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
fs.readFile(fp, (err, data) => {
|
|
82
|
+
if (err) {
|
|
83
|
+
res.statusCode = 404;
|
|
84
|
+
res.end();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (fp.endsWith(".json")) {
|
|
88
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
89
|
+
}
|
|
90
|
+
res.end(data);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/preset.ts
|
|
4
98
|
var dir = dirname(fileURLToPath(import.meta.url));
|
|
5
99
|
function managerEntries(entry = []) {
|
|
6
100
|
return [...entry, join(dir, "manager.js")];
|
|
7
101
|
}
|
|
102
|
+
function escapeRegExp(s) {
|
|
103
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
104
|
+
}
|
|
105
|
+
function aliasEntriesToArray(alias) {
|
|
106
|
+
if (alias === void 0) return [];
|
|
107
|
+
if (Array.isArray(alias)) {
|
|
108
|
+
return [...alias];
|
|
109
|
+
}
|
|
110
|
+
if (typeof alias === "object" && alias !== null) {
|
|
111
|
+
return Object.entries(alias).map(([find, replacement]) => ({
|
|
112
|
+
find,
|
|
113
|
+
replacement
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
function mergeViteResolveAlias(config, legacyPath, replacement) {
|
|
119
|
+
const prevResolve = config.resolve;
|
|
120
|
+
const prevAlias = prevResolve?.alias;
|
|
121
|
+
const { alias: _drop, ...resolveRest } = typeof prevResolve === "object" && prevResolve !== null ? prevResolve : {};
|
|
122
|
+
const entry = {
|
|
123
|
+
find: new RegExp(`^${escapeRegExp(legacyPath)}$`),
|
|
124
|
+
replacement
|
|
125
|
+
};
|
|
126
|
+
const nextAlias = [...aliasEntriesToArray(prevAlias), entry];
|
|
127
|
+
return {
|
|
128
|
+
...config,
|
|
129
|
+
resolve: {
|
|
130
|
+
...resolveRest,
|
|
131
|
+
alias: nextAlias
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
async function viteFinal(config, options) {
|
|
136
|
+
const c = config;
|
|
137
|
+
const withCsp = mergeSxlFigmaFrameSrcHeader(
|
|
138
|
+
c
|
|
139
|
+
);
|
|
140
|
+
const bases = [];
|
|
141
|
+
if (options?.configDir) bases.push(options.configDir);
|
|
142
|
+
bases.push(process.cwd());
|
|
143
|
+
const tokensRoot = resolveSxlTokensRootSync(bases);
|
|
144
|
+
if (!tokensRoot) {
|
|
145
|
+
return withCsp;
|
|
146
|
+
}
|
|
147
|
+
const plugins = c.plugins ?? [];
|
|
148
|
+
let next = {
|
|
149
|
+
...withCsp,
|
|
150
|
+
plugins: [...plugins, createSxlTokensDevPlugin(tokensRoot)]
|
|
151
|
+
};
|
|
152
|
+
const fb = resolveDiffCodeConnectAliasFallback(tokensRoot);
|
|
153
|
+
if (fb) {
|
|
154
|
+
next = mergeViteResolveAlias(next, fb.legacyAbsolutePath, fb.replacementAbsolutePath);
|
|
155
|
+
}
|
|
156
|
+
return next;
|
|
157
|
+
}
|
|
158
|
+
function staticDirs(entries, options) {
|
|
159
|
+
const e = Array.isArray(entries) ? entries : [];
|
|
160
|
+
const bases = [];
|
|
161
|
+
if (options?.configDir) bases.push(options.configDir);
|
|
162
|
+
bases.push(process.cwd());
|
|
163
|
+
const root = resolveSxlTokensRootSync(bases);
|
|
164
|
+
if (!root) return e;
|
|
165
|
+
const mount = SXL_TOKENS_URL_PREFIX.replace(/^\//, "") || "sxl-tokens";
|
|
166
|
+
return [...e, { from: root, to: `/${mount}` }];
|
|
167
|
+
}
|
|
8
168
|
function mergeSxlFigmaFrameSrcHeader(config) {
|
|
9
169
|
const directive = "frame-src 'self' https://www.figma.com https://*.figma.com data: blob:;";
|
|
10
170
|
const prev = config.server?.headers?.["Content-Security-Policy"];
|
|
@@ -22,5 +182,7 @@ function mergeSxlFigmaFrameSrcHeader(config) {
|
|
|
22
182
|
}
|
|
23
183
|
export {
|
|
24
184
|
managerEntries,
|
|
25
|
-
mergeSxlFigmaFrameSrcHeader
|
|
185
|
+
mergeSxlFigmaFrameSrcHeader,
|
|
186
|
+
staticDirs,
|
|
187
|
+
viteFinal
|
|
26
188
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sxl-studio/storybook-addon",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"description": "Storybook addon for SXL Studio — displays Figma Embed, component info and design token status for linked components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
"default": "./dist/index.js"
|
|
12
12
|
},
|
|
13
13
|
"./manager": "./dist/manager.js",
|
|
14
|
-
"./preset": "./
|
|
14
|
+
"./preset": "./preset.js"
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
"dist",
|
|
18
|
-
"README.md"
|
|
18
|
+
"README.md",
|
|
19
|
+
"preset.js"
|
|
19
20
|
],
|
|
20
21
|
"publishConfig": {
|
|
21
22
|
"access": "public"
|
|
@@ -51,6 +52,7 @@
|
|
|
51
52
|
"typescript": "^5.7.0"
|
|
52
53
|
},
|
|
53
54
|
"storybook": {
|
|
55
|
+
"preset": "./preset.js",
|
|
54
56
|
"supportedFrameworks": [
|
|
55
57
|
"react",
|
|
56
58
|
"vue",
|
package/preset.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./dist/preset.js";
|