@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 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.16",
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": "./dist/preset.js"
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";