storybook-addon-dependency-previews 0.9.0 → 0.10.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 +56 -11
- package/dist/cli/sb-deps.cjs +90 -58
- package/dist/cli/sb-deps.cjs.map +1 -1
- package/dist/cli/sb-deps.mjs +89 -57
- package/dist/cli/sb-deps.mjs.map +1 -1
- package/dist/cli/setup/detect.cjs +100 -2
- package/dist/cli/setup/detect.cjs.map +1 -1
- package/dist/cli/setup/detect.d.cts +62 -6
- package/dist/cli/setup/detect.d.mts +62 -6
- package/dist/cli/setup/detect.mjs +100 -4
- package/dist/cli/setup/detect.mjs.map +1 -1
- package/dist/cli/setup/index.cjs +61 -47
- package/dist/cli/setup/index.cjs.map +1 -1
- package/dist/cli/setup/index.mjs +62 -48
- package/dist/cli/setup/index.mjs.map +1 -1
- package/dist/cli/setup/install.cjs +23 -5
- package/dist/cli/setup/install.cjs.map +1 -1
- package/dist/cli/setup/install.d.cts +6 -0
- package/dist/cli/setup/install.d.mts +6 -0
- package/dist/cli/setup/install.mjs +23 -5
- package/dist/cli/setup/install.mjs.map +1 -1
- package/dist/cli/setup/patchers/preview.cjs +1446 -158
- package/dist/cli/setup/patchers/preview.cjs.map +1 -1
- package/dist/cli/setup/patchers/preview.d.cts +13 -2
- package/dist/cli/setup/patchers/preview.d.mts +13 -2
- package/dist/cli/setup/patchers/preview.mjs +1447 -159
- package/dist/cli/setup/patchers/preview.mjs.map +1 -1
- package/dist/cli/setup/patchers/sbDepsConfig.cjs +12 -11
- package/dist/cli/setup/patchers/sbDepsConfig.cjs.map +1 -1
- package/dist/cli/setup/patchers/sbDepsConfig.mjs +12 -11
- package/dist/cli/setup/patchers/sbDepsConfig.mjs.map +1 -1
- package/dist/cli/setup/util.cjs +478 -174
- package/dist/cli/setup/util.cjs.map +1 -1
- package/dist/cli/setup/util.d.cts +130 -8
- package/dist/cli/setup/util.d.mts +130 -8
- package/dist/cli/setup/util.mjs +471 -175
- package/dist/cli/setup/util.mjs.map +1 -1
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +47 -28
- package/dist/config.d.mts +47 -28
- package/dist/config.mjs.map +1 -1
- package/dist/index.cjs +43 -2
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +33 -2
- package/dist/index.d.mts +33 -2
- package/dist/index.mjs +38 -2
- package/dist/index.mjs.map +1 -0
- package/dist/panels/DefaultAutoDocsLayout.d.cts +1 -1
- package/dist/panels/DefaultAutoDocsLayout.d.mts +1 -1
- package/dist/preview.d.mts +1 -1
- package/dist/src/config.d.cts +274 -0
- package/dist/src/config.d.mts +274 -0
- package/dist/types.d.cts +21 -3
- package/dist/types.d.mts +21 -3
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
## What is this?
|
|
6
6
|
|
|
7
|
-
> **This plugin is built for Storybook 10**
|
|
7
|
+
> **This plugin is built for Storybook 10 and 11**
|
|
8
8
|
|
|
9
9
|
A plugin for [Storybook](https://storybook.js.org/) that shows the full dependency tree in both directions (built with and used by) the components in your application.
|
|
10
10
|
|
|
11
|
-
Currently works with **React**, **Svelte**, **Vue 3**, **Solid**, **Angular**, and **Next.js**. The automated `sb-deps setup` wizard handles Vite-based projects (React, Svelte, Vue 3, Solid) end-to-end. Webpack-based projects (Angular, Next.js) need a one-time manual setup — see the [manual-setup-webpack guide](https://github.com/Dan503/storybook-addon-dependency-previews/blob/main/storybook-addon-package/docs/manual-setup-webpack.md) below.
|
|
11
|
+
Currently works with **React**, **Preact**, **Svelte**, **Vue 3**, **Solid**, **Angular**, and **Next.js**. The automated `sb-deps setup` wizard handles Vite-based projects (React, Preact, Svelte, Vue 3, Solid) end-to-end. Webpack-based projects (Angular, Next.js) need a one-time manual setup — see the [manual-setup-webpack guide](https://github.com/Dan503/storybook-addon-dependency-previews/blob/main/storybook-addon-package/docs/manual-setup-webpack.md) below.
|
|
12
12
|
|
|
13
13
|
This is what you will see in Storybook after Dependency Previews have been installed and configured:
|
|
14
14
|
|
|
@@ -26,6 +26,12 @@ The below image demonstrates what you will see when you open up some of the depe
|
|
|
26
26
|
- [React rendered example website](https://dependency-previews-demo-site-react.netlify.app/)
|
|
27
27
|
- [React demo source code](https://github.com/Dan503/storybook-addon-dependency-previews/tree/main/example-site/react)
|
|
28
28
|
|
|
29
|
+
#### Preact demos
|
|
30
|
+
|
|
31
|
+
- [Preact Storybook demo site](https://dependency-previews-storybook-preact.netlify.app/?path=/docs/04-templates-home-template--docs)
|
|
32
|
+
- [Preact rendered example website](https://dependency-previews-demo-site-preact.netlify.app/)
|
|
33
|
+
- [Preact demo source code](https://github.com/Dan503/storybook-addon-dependency-previews/tree/main/example-site/preact)
|
|
34
|
+
|
|
29
35
|
#### Svelte demos
|
|
30
36
|
|
|
31
37
|
- [Svelte Storybook demo site](https://dependency-previews-storybook-svelte.netlify.app/?path=/docs/04-templates-home-template--docs)
|
|
@@ -56,7 +62,7 @@ Vue version built for Vue 3.
|
|
|
56
62
|
|
|
57
63
|
## Installation guide
|
|
58
64
|
|
|
59
|
-
### Quick start (React, Svelte, Vue 3, and Solid)
|
|
65
|
+
### Quick start (React, Preact, Svelte, Vue 3, and Solid)
|
|
60
66
|
|
|
61
67
|
After running `npx storybook@latest init` in your project, run the setup wizard:
|
|
62
68
|
|
|
@@ -98,11 +104,44 @@ When it finishes, run `npm run sb` (or your package manager's equivalent) to sta
|
|
|
98
104
|
|
|
99
105
|
### Manual setup
|
|
100
106
|
|
|
101
|
-
The wizard supports React (`@storybook/react-vite`), Svelte (`@storybook/sveltekit`, `@storybook/svelte-vite`), Vue 3 (`@storybook/vue3-vite`), and Solid (`storybook-solidjs-vite`) — all Vite-based. **Angular (`@storybook/angular`) and Next.js (`@storybook/nextjs`) projects are both webpack-based and require manual setup** — the wizard's preview-patcher relies on Vite's `import.meta.glob`, which webpack doesn't expose. Follow the matching guide below:
|
|
107
|
+
The wizard supports React (`@storybook/react-vite`), Preact (`@storybook/preact-vite`), Svelte (`@storybook/sveltekit`, `@storybook/svelte-vite`), Vue 3 (`@storybook/vue3-vite`), and Solid (`storybook-solidjs-vite`) — all Vite-based. **Angular (`@storybook/angular`) and Next.js (`@storybook/nextjs`) projects are both webpack-based and require manual setup** — the wizard's preview-patcher relies on Vite's `import.meta.glob`, which webpack doesn't expose. Follow the matching guide below:
|
|
102
108
|
|
|
103
|
-
- [Manual setup — Vite (React, Svelte, Vue 3, Solid)](https://github.com/Dan503/storybook-addon-dependency-previews/blob/main/storybook-addon-package/docs/manual-setup-vite.md)
|
|
109
|
+
- [Manual setup — Vite (React, Preact, Svelte, Vue 3, Solid)](https://github.com/Dan503/storybook-addon-dependency-previews/blob/main/storybook-addon-package/docs/manual-setup-vite.md)
|
|
104
110
|
- [Manual setup — webpack (`@storybook/angular`, `@storybook/nextjs`)](https://github.com/Dan503/storybook-addon-dependency-previews/blob/main/storybook-addon-package/docs/manual-setup-webpack.md)
|
|
105
111
|
|
|
112
|
+
### Storybook 11 / CSF Next preview configs
|
|
113
|
+
|
|
114
|
+
From Storybook 11 the default `.storybook/preview.ts` style is CSF Next — a `definePreview({ ... })` call with an `addons` list. Register the addon there by calling the `dependencyPreviews()` function the package exports, alongside `@storybook/addon-docs`:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
/// <reference types="vite/client" />
|
|
118
|
+
|
|
119
|
+
import { definePreview } from '@storybook/react-vite'
|
|
120
|
+
import addonDocs from '@storybook/addon-docs'
|
|
121
|
+
import { dependencyPreviews } from 'storybook-addon-dependency-previews'
|
|
122
|
+
|
|
123
|
+
import dependenciesJson from './dependency-previews.json'
|
|
124
|
+
|
|
125
|
+
export default definePreview({
|
|
126
|
+
addons: [addonDocs(), dependencyPreviews()],
|
|
127
|
+
parameters: {
|
|
128
|
+
// The same settings block as the hand-spread form — the manual setup
|
|
129
|
+
// guides explain each value.
|
|
130
|
+
dependencyPreviews: {
|
|
131
|
+
dependenciesJson,
|
|
132
|
+
storyModules: import.meta.glob(
|
|
133
|
+
'/src/**/*.{story,stories}.{tsx,ts,jsx,js,svelte}',
|
|
134
|
+
{ eager: false },
|
|
135
|
+
),
|
|
136
|
+
sourceRootUrl: 'https://github.com/your-org/your-repo/blob/main',
|
|
137
|
+
projectRootPath: new URL('..', import.meta.url).pathname,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
})
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The setup wizard recognises both preview styles: an existing `definePreview({ ... })` file gets `addonDocs()` and `dependencyPreviews()` added to its `addons` list and the settings block added to its `parameters`, and a classic `const preview = { ... }` file gets the hand-spread form (spreading `defaultPreviewParameters` and `dependencyPreviewDecorators`), which remains supported. When there is no preview file yet, the wizard writes the `definePreview` style on Storybook 11 for frameworks whose package exports `definePreview` (React, Vue 3 and Solid), and the hand-spread style everywhere else — it works on both majors. Both manual setup guides show the full CSF Next config for their frameworks.
|
|
144
|
+
|
|
106
145
|
## Auto-scaffolding new components and stories
|
|
107
146
|
|
|
108
147
|
While `sb-deps` is watching (`npm run sb`), creating an **empty** source file fills it in from a template — and creates its matching sibling too. It works from either side:
|
|
@@ -110,9 +149,9 @@ While `sb-deps` is watching (`npm run sb`), creating an **empty** source file fi
|
|
|
110
149
|
- **Create a component file** (`Button.tsx`, `Button.svelte`, `Button.vue`, `Button.component.ts`) → the component body is scaffolded **and** a matching story file is generated next to it.
|
|
111
150
|
- **Create a story file** (`Button.stories.tsx`, or the singular `Button.story.tsx`) → the story is scaffolded into that exact file, and if the sibling component doesn't exist yet it is created and scaffolded too.
|
|
112
151
|
|
|
113
|
-
Either way you end up with a working component + story pair. Only empty files are touched, so existing files are never overwritten. A `.stories.ts` with no component beside it is resolved to React, Solid, Vue, or Angular from your project's framework (Svelte stories use a `.svelte` file, so `.ts` isn't scaffolded for Svelte).
|
|
152
|
+
Either way you end up with a working component + story pair. Only empty files are touched, so existing files are never overwritten. A `.stories.ts` with no component beside it is resolved to React, Preact, Solid, Vue, or Angular from your project's framework (Svelte stories use a `.svelte` file, so `.ts` isn't scaffolded for Svelte).
|
|
114
153
|
|
|
115
|
-
React and Solid
|
|
154
|
+
React, Preact and Solid all author components in `.tsx`, so the extension alone can't tell them apart. `sb-deps` works it out from your project, so a Solid project gets Solid templates (`solid-js`, `storybook-solidjs-vite`) and a Preact project gets Preact ones (`preact/hooks`, `@storybook/preact-vite`) without being told; anything it reads as neither gets React templates. Set `tsxFramework` in your `sb-deps` config to say so outright — worth doing where your project's framework isn't obvious from its files.
|
|
116
155
|
|
|
117
156
|
### What the scaffolded components assume
|
|
118
157
|
|
|
@@ -122,7 +161,7 @@ Each generated component is written the way its framework currently recommends,
|
|
|
122
161
|
- **Svelte 5** — `$props()`, `$state()` and the `Snippet` type.
|
|
123
162
|
- **Angular 17.1+** — `input()` signals.
|
|
124
163
|
|
|
125
|
-
React and Solid add no version floor beyond what the addon itself needs. In a **Next.js** project the React component is written with a `'use client'` line at the top, because it holds state and the App Router renders on the server; you will not see that line in any other React project.
|
|
164
|
+
React, Preact and Solid add no version floor beyond what the addon itself needs. In a **Next.js** project the React component is written with a `'use client'` line at the top, because it holds state and the App Router renders on the server; you will not see that line in any other React project.
|
|
126
165
|
|
|
127
166
|
None of this affects the dependency graph or anything else `sb-deps` does — it only describes the starter code it writes, so on an older version replace the generated body with whatever your project uses. Or replace the template outright via [`scaffold`](#scaffold), which is what that option is for.
|
|
128
167
|
|
|
@@ -250,9 +289,9 @@ export default defineSbDepsConfig({
|
|
|
250
289
|
|
|
251
290
|
### `tsxFramework`
|
|
252
291
|
|
|
253
|
-
Which flavor to scaffold for `.tsx` component and story files — `'react'` or `'
|
|
292
|
+
Which flavor to scaffold for `.tsx` component and story files — `'react'`, `'solid'` or `'preact'`. All three author components in `.tsx`, so the extension alone can't tell them apart; set this to `'solid'` in a Solid project and scaffolded `.tsx` files get Solid templates (`solid-js` `createSignal`/`mergeProps`, `storybook-solidjs-vite` story imports), or to `'preact'` in a Preact project for Preact ones (`preact/hooks` `useState`, `@storybook/preact-vite` story imports), instead of React. The setup wizard sets it for you when it detects a Solid or Preact project. Per-template overrides go under the matching [`scaffold.solid` / `scaffold.preact`](#scaffold) key.
|
|
254
293
|
|
|
255
|
-
**Default:** `'solid'` when `sb-deps` detects a Solid project, `'react'` otherwise — so
|
|
294
|
+
**Default:** `'solid'` when `sb-deps` detects a Solid project, `'preact'` when it detects a Preact one, `'react'` otherwise — so either gets its own templates without the key being set. Setting the key is worth it where the framework isn't obvious from your project's files.
|
|
256
295
|
|
|
257
296
|
```js
|
|
258
297
|
// sb-deps.config.mjs
|
|
@@ -303,7 +342,7 @@ An entry that isn't a non-empty string makes the whole option invalid — the CL
|
|
|
303
342
|
|
|
304
343
|
Override the templates used when `sb-deps` auto-scaffolds new component and story files. Each template function receives a context object with relevant variables and must return the full file content as a string.
|
|
305
344
|
|
|
306
|
-
For `.tsx` files the override key follows [`tsxFramework`](#tsxframework): a React project reads `scaffold.react`, a Solid project reads `scaffold.solid` — overrides placed under the wrong key are silently ignored.
|
|
345
|
+
For `.tsx` files the override key follows [`tsxFramework`](#tsxframework): a React project reads `scaffold.react`, a Solid project reads `scaffold.solid`, a Preact project reads `scaffold.preact` — overrides placed under the wrong key are silently ignored.
|
|
307
346
|
|
|
308
347
|
```js
|
|
309
348
|
// sb-deps.config.mjs
|
|
@@ -349,6 +388,12 @@ export function ${componentName}({}: ${propsName}) {
|
|
|
349
388
|
/** Customize the generated .stories.tsx file */
|
|
350
389
|
story: ({ componentName, propsName, title, tags, base }) => '...',
|
|
351
390
|
},
|
|
391
|
+
preact: {
|
|
392
|
+
/** Customize the generated .tsx component file */
|
|
393
|
+
component: ({ componentName, propsName }) => '...',
|
|
394
|
+
/** Customize the generated .stories.tsx file */
|
|
395
|
+
story: ({ componentName, propsName, title, tags, base }) => '...',
|
|
396
|
+
},
|
|
352
397
|
angular: {
|
|
353
398
|
/** Customize the generated .component.ts file */
|
|
354
399
|
component: ({
|
package/dist/cli/sb-deps.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
3
3
|
const require_fileNames = require('./scripts/fileNames.cjs');
|
|
4
|
+
const require_cli_setup_util = require('./setup/util.cjs');
|
|
4
5
|
const require_cli_setup_detect = require('./setup/detect.cjs');
|
|
5
6
|
const require_cli_setup_index = require('./setup/index.cjs');
|
|
6
7
|
let node_fs = require("node:fs");
|
|
@@ -100,10 +101,21 @@ async function loadSbDepsConfig() {
|
|
|
100
101
|
function validateConfigChoice(fieldName, value, allowed, fallback) {
|
|
101
102
|
if (value === undefined) return fallback;
|
|
102
103
|
if (allowed.includes(value)) return value;
|
|
103
|
-
|
|
104
|
-
error(`${fieldName} "${value}" is invalid — must be ${choices}. Falling back to '${fallback}'.`);
|
|
104
|
+
error(`${fieldName} "${value}" is invalid — must be ${getChoicesPhrase(allowed)}. Falling back to '${fallback}'.`);
|
|
105
105
|
return fallback;
|
|
106
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* The allowed values as they read in a sentence: `'a'`, `'a' or 'b'`, or
|
|
109
|
+
* `'a', 'b' or 'c'`. Joining every pair with "or" reads as a stutter past two
|
|
110
|
+
* values, which `tsxFramework` now has.
|
|
111
|
+
*/
|
|
112
|
+
function getChoicesPhrase(choices) {
|
|
113
|
+
const quotedChoices = choices.map((choice) => `'${choice}'`);
|
|
114
|
+
const lastChoice = quotedChoices.at(-1);
|
|
115
|
+
if (quotedChoices.length < 2 || !lastChoice) return quotedChoices.join("");
|
|
116
|
+
const earlierChoices = quotedChoices.slice(0, -1).join(", ");
|
|
117
|
+
return `${earlierChoices} or ${lastChoice}`;
|
|
118
|
+
}
|
|
107
119
|
let ANGULAR_SELECTOR_PREFIX = "app-";
|
|
108
120
|
let SCAFFOLD_CONFIG = {};
|
|
109
121
|
let SRC_DIR = "src";
|
|
@@ -117,23 +129,25 @@ function getProjectFramework() {
|
|
|
117
129
|
}
|
|
118
130
|
const IS_WIN = process.platform === "win32";
|
|
119
131
|
/**
|
|
120
|
-
* Locate the `dependency-cruiser` CLI
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
* found — caller decides what to do.
|
|
132
|
+
* Locate the JavaScript entry file of the `dependency-cruiser` CLI the
|
|
133
|
+
* project installed (its `bin.depcruise`), found by walking `node_modules`
|
|
134
|
+
* upwards from the project root so a hoisted workspace install is covered
|
|
135
|
+
* too. Falls back to `null` if it can't be found — caller decides what to do.
|
|
124
136
|
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* avoid.
|
|
137
|
+
* Running that file under the current `node` directly, rather than the
|
|
138
|
+
* `node_modules/.bin/depcruise` shim, means the exact installed
|
|
139
|
+
* `dependency-cruiser` runs with each flag as its own array element and no
|
|
140
|
+
* shell on any platform. On Windows the shim is a `.cmd` file that needs
|
|
141
|
+
* `shell: true`, and cmd.exe then mangles arguments (`^` is its escape
|
|
142
|
+
* character, spaces split a path, `%NAME%` is expanded even inside quotes);
|
|
143
|
+
* skipping the shim removes all of that.
|
|
133
144
|
*/
|
|
134
|
-
function
|
|
135
|
-
const
|
|
136
|
-
|
|
145
|
+
function resolveDepCruiseEntry() {
|
|
146
|
+
const found = require_cli_setup_util.findInstalledPackage(projectRoot, "dependency-cruiser");
|
|
147
|
+
const bin = found?.pkg.bin;
|
|
148
|
+
const relativeEntry = bin && typeof bin === "object" ? bin.depcruise : undefined;
|
|
149
|
+
if (!found || typeof relativeEntry !== "string") return null;
|
|
150
|
+
return (0, node_path.join)(found.dir, relativeEntry);
|
|
137
151
|
}
|
|
138
152
|
function runDepCruiseOnce() {
|
|
139
153
|
const escapedSrcDir = escapeForFolderRegex(SRC_DIR);
|
|
@@ -145,12 +159,12 @@ function runDepCruiseOnce() {
|
|
|
145
159
|
args.push("--no-config");
|
|
146
160
|
}
|
|
147
161
|
args.push("--include-only", includeOnly, "--output-type", "json");
|
|
148
|
-
const
|
|
149
|
-
if (!
|
|
150
|
-
throw new Error("Could not locate `dependency-cruiser` in node_modules
|
|
162
|
+
const depcruiseEntry = resolveDepCruiseEntry();
|
|
163
|
+
if (!depcruiseEntry) {
|
|
164
|
+
throw new Error("Could not locate `dependency-cruiser` in node_modules. Run the setup wizard (`sb-deps setup`) or install `dependency-cruiser` as a dev dependency.");
|
|
151
165
|
}
|
|
152
166
|
const start = Date.now();
|
|
153
|
-
const stdout = (0, node_child_process.execFileSync)(
|
|
167
|
+
const stdout = (0, node_child_process.execFileSync)(process.execPath, [depcruiseEntry, ...args], {
|
|
154
168
|
cwd: projectRoot,
|
|
155
169
|
stdio: [
|
|
156
170
|
"ignore",
|
|
@@ -158,7 +172,6 @@ function runDepCruiseOnce() {
|
|
|
158
172
|
"inherit"
|
|
159
173
|
],
|
|
160
174
|
encoding: "utf8",
|
|
161
|
-
shell: IS_WIN,
|
|
162
175
|
env: {
|
|
163
176
|
...process.env,
|
|
164
177
|
SB_DEPS_SRC_DIR: SRC_DIR
|
|
@@ -194,7 +207,7 @@ function postprocessOnce() {
|
|
|
194
207
|
* exact, so a flag would loosen the half that must not loosen.
|
|
195
208
|
*/
|
|
196
209
|
function escapeForFolderRegex(folderName) {
|
|
197
|
-
return IS_CASE_INSENSITIVE_PATH_FS ? escapeForRegexIgnoringCase(folderName) : escapeForRegex(folderName);
|
|
210
|
+
return IS_CASE_INSENSITIVE_PATH_FS ? escapeForRegexIgnoringCase(folderName) : require_cli_setup_util.escapeForRegex(folderName);
|
|
198
211
|
}
|
|
199
212
|
/**
|
|
200
213
|
* Like `escapeForRegex`, but every letter becomes a pair matching both of its
|
|
@@ -204,24 +217,9 @@ function escapeForFolderRegex(folderName) {
|
|
|
204
217
|
function escapeForRegexIgnoringCase(text) {
|
|
205
218
|
return text.split("").map((char) => {
|
|
206
219
|
const isLetter = /[a-z]/i.test(char);
|
|
207
|
-
return isLetter ? `[${char.toLowerCase()}${char.toUpperCase()}]` : escapeForRegex(char);
|
|
220
|
+
return isLetter ? `[${char.toLowerCase()}${char.toUpperCase()}]` : require_cli_setup_util.escapeForRegex(char);
|
|
208
221
|
}).join("");
|
|
209
222
|
}
|
|
210
|
-
/** Backslash-escape every character that has a special meaning in a regex, so the text only matches itself. */
|
|
211
|
-
function escapeForRegex(text) {
|
|
212
|
-
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Escape an argument so that it survives `cmd.exe` parsing when `execFileSync`
|
|
216
|
-
* is invoked with `shell: true` on Windows. `^` is the cmd.exe escape character,
|
|
217
|
-
* even inside double quotes — doubling it makes cmd.exe pass through a literal
|
|
218
|
-
* `^`. Other shell metacharacters (`&`, `|`, `<`, `>`, `(`, `)`, `%`, `!`) are
|
|
219
|
-
* already wrapped in double quotes by Node's internal arg-quoter when
|
|
220
|
-
* `shell: true`, so we only need to handle `^` ourselves.
|
|
221
|
-
*/
|
|
222
|
-
function escapeForCmdExe(arg) {
|
|
223
|
-
return arg.replace(/\^/g, "^^");
|
|
224
|
-
}
|
|
225
223
|
function buildOnce() {
|
|
226
224
|
try {
|
|
227
225
|
runDepCruiseOnce();
|
|
@@ -698,7 +696,7 @@ function makeTitleFromComponent(absCompPath, base) {
|
|
|
698
696
|
}
|
|
699
697
|
/**
|
|
700
698
|
* The default `.tsx` component template, in the flavor `TSX_FRAMEWORK` selects.
|
|
701
|
-
* React and
|
|
699
|
+
* React, Solid and Preact share the `.tsx` extension but need different code.
|
|
702
700
|
*/
|
|
703
701
|
function tsxComponentTemplate(flavor, componentName, propsName) {
|
|
704
702
|
if (flavor === "solid") {
|
|
@@ -730,12 +728,39 @@ export function ${componentName}(props: ${propsName}) {
|
|
|
730
728
|
}
|
|
731
729
|
`;
|
|
732
730
|
}
|
|
731
|
+
if (flavor === "preact") {
|
|
732
|
+
return reactLikeComponentTemplate({
|
|
733
|
+
componentName,
|
|
734
|
+
propsName,
|
|
735
|
+
imports: `import type { ComponentChildren } from 'preact'\nimport { useState } from 'preact/hooks'\n`,
|
|
736
|
+
childrenType: "ComponentChildren",
|
|
737
|
+
classAttribute: "class"
|
|
738
|
+
});
|
|
739
|
+
}
|
|
733
740
|
const clientDirective = getProjectFramework() === "nextjs-webpack" ? "'use client'\n\n" : "";
|
|
734
|
-
return
|
|
735
|
-
|
|
741
|
+
return reactLikeComponentTemplate({
|
|
742
|
+
componentName,
|
|
743
|
+
propsName,
|
|
744
|
+
imports: `${clientDirective}import { useState, type ReactNode } from 'react'\n`,
|
|
745
|
+
childrenType: "ReactNode",
|
|
746
|
+
classAttribute: "className"
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* The `.tsx` component template React and Preact both emit. The two frameworks
|
|
751
|
+
* write the same component — destructured props with defaults, a counter, a
|
|
752
|
+
* children slot — and differ only in the pieces named above, so the body is
|
|
753
|
+
* written once here rather than twice.
|
|
754
|
+
*
|
|
755
|
+
* Solid is not written from this. Its component reads its props through
|
|
756
|
+
* `mergeProps` instead of destructuring them, so it shares no body to speak of
|
|
757
|
+
* and keeps its own template.
|
|
758
|
+
*/
|
|
759
|
+
function reactLikeComponentTemplate({ componentName, propsName, imports, childrenType, classAttribute }) {
|
|
760
|
+
return `${imports}
|
|
736
761
|
export interface ${propsName} {
|
|
737
762
|
text?: string
|
|
738
|
-
children?:
|
|
763
|
+
children?: ${childrenType}
|
|
739
764
|
}
|
|
740
765
|
|
|
741
766
|
export function ${componentName}({
|
|
@@ -745,7 +770,7 @@ export function ${componentName}({
|
|
|
745
770
|
const [count, setCount] = useState(0)
|
|
746
771
|
|
|
747
772
|
return (
|
|
748
|
-
<div
|
|
773
|
+
<div ${classAttribute}="${componentName}">
|
|
749
774
|
<p>{text}</p>
|
|
750
775
|
<button type="button" onClick={() => setCount(count + 1)}>
|
|
751
776
|
count: {count}
|
|
@@ -757,8 +782,8 @@ export function ${componentName}({
|
|
|
757
782
|
`;
|
|
758
783
|
}
|
|
759
784
|
/**
|
|
760
|
-
* The default `.tsx` story template. React and
|
|
761
|
-
* from which package the Storybook types come from (see
|
|
785
|
+
* The default `.tsx` story template. React, Solid and Preact stories are
|
|
786
|
+
* identical apart from which package the Storybook types come from (see
|
|
762
787
|
* `getTsxStoryTypesPackage`).
|
|
763
788
|
*
|
|
764
789
|
* The component is imported from `./${base}` (the actual filename), not
|
|
@@ -792,13 +817,14 @@ export const Primary: Story = {
|
|
|
792
817
|
}
|
|
793
818
|
/**
|
|
794
819
|
* The package a scaffolded `.tsx` story imports its Storybook types from. Solid
|
|
795
|
-
*
|
|
796
|
-
* project's Storybook types live in `@storybook/nextjs`, not in the Vite
|
|
820
|
+
* and Preact each have their own. React's depends on how the project builds: a
|
|
821
|
+
* Next.js project's Storybook types live in `@storybook/nextjs`, not in the Vite
|
|
797
822
|
* package, so importing the Vite one there produces a story that doesn't
|
|
798
823
|
* type-check.
|
|
799
824
|
*/
|
|
800
825
|
function getTsxStoryTypesPackage(flavor) {
|
|
801
826
|
if (flavor === "solid") return "storybook-solidjs-vite";
|
|
827
|
+
if (flavor === "preact") return "@storybook/preact-vite";
|
|
802
828
|
if (getProjectFramework() === "nextjs-webpack") return "@storybook/nextjs";
|
|
803
829
|
return "@storybook/react-vite";
|
|
804
830
|
}
|
|
@@ -1382,7 +1408,8 @@ function getFrameworkFamily(framework) {
|
|
|
1382
1408
|
switch (framework) {
|
|
1383
1409
|
case "react-vite":
|
|
1384
1410
|
case "nextjs-webpack":
|
|
1385
|
-
case "solid-vite":
|
|
1411
|
+
case "solid-vite":
|
|
1412
|
+
case "preact-vite": return "react";
|
|
1386
1413
|
case "vue3-vite": return "vue";
|
|
1387
1414
|
case "sveltekit":
|
|
1388
1415
|
case "svelte-vite": return "svelte";
|
|
@@ -1446,9 +1473,10 @@ function ensureStoryFor(framework, absCompPath) {
|
|
|
1446
1473
|
}
|
|
1447
1474
|
/**
|
|
1448
1475
|
* Work out the component a created story file belongs to, and which framework's
|
|
1449
|
-
* scaffolders to use. `.tsx` → React or
|
|
1450
|
-
* React, Solid, Vue, or Angular (disambiguated in
|
|
1451
|
-
*
|
|
1476
|
+
* scaffolders to use. `.tsx` → React, Solid or Preact, `.svelte` → Svelte,
|
|
1477
|
+
* `.ts` → React, Solid, Preact, Vue, or Angular (disambiguated in
|
|
1478
|
+
* `resolveTsStoryComponent`). Every framework that writes `.tsx` shares that
|
|
1479
|
+
* route, and they are told apart by `tsxFramework`.
|
|
1452
1480
|
* Any extension with no entry in `STORY_COMPONENT_RESOLVERS` returns `null` —
|
|
1453
1481
|
* not something we scaffold.
|
|
1454
1482
|
*/
|
|
@@ -1541,11 +1569,11 @@ function getComponentForStoryByExtension(absStoryPath) {
|
|
|
1541
1569
|
return resolveComponent(storyBase, absStoryPath);
|
|
1542
1570
|
}
|
|
1543
1571
|
/**
|
|
1544
|
-
* A `.stories.ts` story can be React, Solid, Vue, or Angular — all of
|
|
1545
|
-
* `.ts` story files (the React and
|
|
1546
|
-
* they're valid as `.ts` even though
|
|
1547
|
-
* convention). Prefer an existing sibling component to decide
|
|
1548
|
-
* (`<base>.tsx` → React or
|
|
1572
|
+
* A `.stories.ts` story can be React, Solid, Preact, Vue, or Angular — all of
|
|
1573
|
+
* them use `.ts` story files (the React, Solid and Preact scaffolded templates
|
|
1574
|
+
* are JSX-free, so they're valid as `.ts` even though all three write stories as
|
|
1575
|
+
* `.tsx` by convention). Prefer an existing sibling component to decide
|
|
1576
|
+
* (`<base>.tsx` → React, Solid or Preact, told apart by `tsxFramework`;
|
|
1549
1577
|
* `<base>.vue` → Vue; `<base>.component.ts` → Angular); with none present,
|
|
1550
1578
|
* fall back to the project's detected framework. Svelte is intentionally excluded: its story
|
|
1551
1579
|
* template is `.svelte`-specific, so a `.ts` Svelte story can't be scaffolded
|
|
@@ -1920,7 +1948,11 @@ async function startStorybook() {
|
|
|
1920
1948
|
}
|
|
1921
1949
|
}
|
|
1922
1950
|
STORYBOOK_FILE_EXTENSION = validateConfigChoice("storybookFileExtension", cfg.storybookFileExtension, ["story", "stories"], "stories");
|
|
1923
|
-
TSX_FRAMEWORK = validateConfigChoice("tsxFramework", cfg.tsxFramework, [
|
|
1951
|
+
TSX_FRAMEWORK = validateConfigChoice("tsxFramework", cfg.tsxFramework, [
|
|
1952
|
+
"react",
|
|
1953
|
+
"solid",
|
|
1954
|
+
"preact"
|
|
1955
|
+
], require_cli_setup_detect.tsxFrameworkFromFramework(getProjectFramework()));
|
|
1924
1956
|
const configuredScaffoldIgnore = cfg.scaffoldIgnore;
|
|
1925
1957
|
const isScaffoldIgnoreUsable = Array.isArray(configuredScaffoldIgnore) && configuredScaffoldIgnore.every((pattern) => typeof pattern === "string" && pattern.trim() !== "");
|
|
1926
1958
|
if (configuredScaffoldIgnore === undefined) {
|