poveste 0.18.2 → 0.19.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 +5 -1
- package/dist/node/bin.js +1 -0
- package/dist/node/collect/index.js +4 -0
- package/dist/node/collect/layout-keys.d.ts +1 -0
- package/dist/node/collect/layout-keys.js +33 -0
- package/dist/node/collect/logger.d.ts +12 -0
- package/dist/node/collect/logger.js +19 -0
- package/dist/node/collect/runner.js +5 -1
- package/dist/node/commands/port.d.ts +18 -6
- package/dist/node/commands/port.js +18 -8
- package/dist/node/commands/preview.d.ts +1 -0
- package/dist/node/commands/preview.js +4 -0
- package/dist/node/config-validation.d.ts +1 -1
- package/dist/node/config-validation.js +12 -0
- package/dist/node/config.d.ts +1 -0
- package/dist/node/config.js +7 -2
- package/dist/node/dev-event.d.ts +23 -0
- package/dist/node/dev-event.js +42 -0
- package/dist/node/index.d.ts +1 -1
- package/dist/node/index.js +3 -1
- package/dist/node/server.js +19 -18
- package/dist/node/vendors.d.ts +8 -2
- package/dist/node/vendors.js +12 -5
- package/dist/node/virtual/resolved-stories.js +24 -6
- package/dist/node/vite.js +17 -6
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
Poveste builds a browsable *book* of stories from your components, powered by
|
|
6
6
|
[Vite](https://vite.dev). It is a drop-in fork of
|
|
7
7
|
[histoire](https://github.com/histoire-dev/histoire), covering Vue, Nuxt, Svelte,
|
|
8
|
-
SvelteKit and Quasar — the last of which histoire does not support.
|
|
8
|
+
SvelteKit and Quasar — the last of which histoire does not support. Solid and React
|
|
9
|
+
are supported as renderers only: their stories render, and they have no controls
|
|
10
|
+
panel.
|
|
9
11
|
|
|
10
12
|
<img src="https://raw.githubusercontent.com/poveste-dev/poveste/main/screenshot.png" alt="A poveste book: the story list, a variant grid, and the controls panel" width="900">
|
|
11
13
|
|
|
@@ -18,6 +20,8 @@ pnpm i -D poveste @poveste/plugin-vue # Vue 3
|
|
|
18
20
|
pnpm i -D poveste @poveste/plugin-svelte # Svelte 5 and SvelteKit
|
|
19
21
|
pnpm i -D poveste @poveste/plugin-nuxt # Nuxt 4
|
|
20
22
|
pnpm i -D poveste @poveste/plugin-quasar # Quasar
|
|
23
|
+
pnpm i -D poveste @poveste/plugin-solid # Solid, renderer only
|
|
24
|
+
pnpm i -D poveste @poveste/plugin-react # React 19, renderer only
|
|
21
25
|
```
|
|
22
26
|
|
|
23
27
|
> **Installed an older version than you expected?** For about a day after a release, pnpm installs the previous one with no error and no hint, and for a plugin that's new in that release, its empty `0.0.1` placeholder. That's pnpm's release-age cooldown, not a broken publish. `package.json` keeps the old `^x.y.z`, so waiting doesn't fix it: install again with `npm`, with `--config.minimum-release-age=0` (kebab-case: pnpm 12.4 ignores the camelCase spelling), or with exact versions.
|
package/dist/node/bin.js
CHANGED
|
@@ -84,6 +84,7 @@ program.command('build')
|
|
|
84
84
|
program.command('preview')
|
|
85
85
|
.describe('preview the built directory')
|
|
86
86
|
.option('-p, --port <port>', 'Listening port of the server')
|
|
87
|
+
.option('-c, --config <file>', `[string] use specified config file`)
|
|
87
88
|
.option('--open', 'Open in your default browser')
|
|
88
89
|
.option('--host [host]', '[string] specify hostname (omit value or pass `0.0.0.0` to expose to all network interfaces)')
|
|
89
90
|
.action(async (options) => {
|
|
@@ -6,6 +6,7 @@ import { createPath } from '../tree.js';
|
|
|
6
6
|
import { escapeRegExp } from '../util/escape-regexp.js';
|
|
7
7
|
import { slash } from '../util/fs.js';
|
|
8
8
|
import { globalsFromDefine } from './define-globals.js';
|
|
9
|
+
import { strayLayoutKeys } from './layout-keys.js';
|
|
9
10
|
import { createModuleServer } from './module-server.js';
|
|
10
11
|
import { createPool } from './pool.js';
|
|
11
12
|
import { INVALIDATE, INVALIDATE_ALL } from './task.js';
|
|
@@ -82,6 +83,9 @@ export function useCollectStories(options, ctx) {
|
|
|
82
83
|
}
|
|
83
84
|
}
|
|
84
85
|
}
|
|
86
|
+
for (const stray of strayLayoutKeys(finalData.layout)) {
|
|
87
|
+
console.warn(pc.yellow(`⚠️ ${storyFile.path}: ${stray}`));
|
|
88
|
+
}
|
|
85
89
|
if (!finalData.layout) {
|
|
86
90
|
finalData.layout = { type: 'single', iframe: true };
|
|
87
91
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function strayLayoutKeys(layout: unknown): string[];
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The keys of a story's `layout` that its `type` does not read.
|
|
3
|
+
*
|
|
4
|
+
* `StoryLayout` is a union, and its two members spell one idea two ways:
|
|
5
|
+
* `iframe` on a single layout, `iframeGrid` on a grid. A key from the other
|
|
6
|
+
* member is carried through untouched and read by nothing, so
|
|
7
|
+
* `{ type: 'grid', iframe: false }` got iframes and nobody was told (#333).
|
|
8
|
+
*/
|
|
9
|
+
const READ_BY = {
|
|
10
|
+
single: ['iframe', 'isolate'],
|
|
11
|
+
grid: ['width', 'iframeGrid', 'isolate'],
|
|
12
|
+
};
|
|
13
|
+
/** What the author of a stray key meant, where the other layout has a key for it. */
|
|
14
|
+
const MEANT = {
|
|
15
|
+
single: { iframeGrid: 'iframe' },
|
|
16
|
+
grid: { iframe: 'iframeGrid' },
|
|
17
|
+
};
|
|
18
|
+
export function strayLayoutKeys(layout) {
|
|
19
|
+
if (!layout || typeof layout !== 'object') {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
const { type, ...rest } = layout;
|
|
23
|
+
const read = READ_BY[String(type)];
|
|
24
|
+
if (!read) {
|
|
25
|
+
return [`layout.type is ${JSON.stringify(type)}, and a layout is 'single' or 'grid'`];
|
|
26
|
+
}
|
|
27
|
+
return Object.keys(rest)
|
|
28
|
+
.filter(key => !read.includes(key))
|
|
29
|
+
.map((key) => {
|
|
30
|
+
const meant = MEANT[String(type)]?.[key];
|
|
31
|
+
return `layout.${key} is not read by a ${type} layout, so it does nothing${meant ? `: use ${meant}` : ''}`;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Logger } from 'vite';
|
|
2
|
+
/**
|
|
3
|
+
* The collection server's logger: `base`, less one message.
|
|
4
|
+
*
|
|
5
|
+
* Collection runs its modules in Node, through an environment Vite treats as a
|
|
6
|
+
* browser's. A package's Node build importing `node:http` is then reported as
|
|
7
|
+
* "externalized for browser compatibility", which is false of where it runs: 25
|
|
8
|
+
* lines from `ofetch` in a Nuxt book, reading as errors about a build that worked
|
|
9
|
+
* (#1203). The same line from the book's own server or build is about the
|
|
10
|
+
* browser, and is left alone.
|
|
11
|
+
*/
|
|
12
|
+
export declare function collectionLogger(base: Logger): Logger;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The collection server's logger: `base`, less one message.
|
|
3
|
+
*
|
|
4
|
+
* Collection runs its modules in Node, through an environment Vite treats as a
|
|
5
|
+
* browser's. A package's Node build importing `node:http` is then reported as
|
|
6
|
+
* "externalized for browser compatibility", which is false of where it runs: 25
|
|
7
|
+
* lines from `ofetch` in a Nuxt book, reading as errors about a build that worked
|
|
8
|
+
* (#1203). The same line from the book's own server or build is about the
|
|
9
|
+
* browser, and is left alone.
|
|
10
|
+
*/
|
|
11
|
+
export function collectionLogger(base) {
|
|
12
|
+
const quiet = (log) => (message, options) => {
|
|
13
|
+
if (!message.includes('has been externalized for browser compatibility')) {
|
|
14
|
+
log(message, options);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
// Assigned onto `base`, not spread from it: `hasWarned` is state its own methods set.
|
|
18
|
+
return Object.assign(base, { warn: quiet(base.warn.bind(base)), warnOnce: quiet(base.warnOnce.bind(base)) });
|
|
19
|
+
}
|
|
@@ -122,7 +122,11 @@ class InteropEvaluator {
|
|
|
122
122
|
assigned = true;
|
|
123
123
|
},
|
|
124
124
|
};
|
|
125
|
-
|
|
125
|
+
// In a block of its own, so a module's top-level `const require = createRequire(…)`
|
|
126
|
+
// or `const __dirname = …` shadows the parameter of that name. In the function's
|
|
127
|
+
// own scope it redeclared it, a syntax error that failed every story of the book
|
|
128
|
+
// (#1301). The closing brace goes on a new line: the code may end in a line comment.
|
|
129
|
+
const run = new AsyncFunction(ssrModuleExportsKey, ssrImportMetaKey, ssrImportKey, ssrDynamicImportKey, ssrExportAllKey, ssrExportNameKey, 'require', 'exports', 'module', '__filename', '__dirname', `"use strict";{\n${code}\n}`);
|
|
126
130
|
await run(context[ssrModuleExportsKey], context[ssrImportMetaKey], context[ssrImportKey], context[ssrDynamicImportKey], context[ssrExportAllKey], context[ssrExportNameKey], createRequire(filename), cjsExports, moduleProxy, filename, dirname(filename));
|
|
127
131
|
}
|
|
128
132
|
async runExternalModule(file) {
|
|
@@ -7,11 +7,23 @@
|
|
|
7
7
|
* neither the flag nor the mistake. A non-numeric value fails the same way.
|
|
8
8
|
*/
|
|
9
9
|
export declare function resolvePort(value: unknown, command: string): number | undefined;
|
|
10
|
+
type SocketOptions = Record<string, unknown>;
|
|
10
11
|
/**
|
|
11
|
-
* The HMR socket
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* The book server's HMR socket settings: whatever the merged config asked for,
|
|
13
|
+
* minus a port or server of its own, so Vite serves the socket on the book's port
|
|
14
|
+
* and the browser dials the page's own origin. Vite takes these from `server.ws`
|
|
15
|
+
* and from the deprecated `server.hmr` alike, so both are cleared.
|
|
16
|
+
*
|
|
17
|
+
* A socket on a second port was never routed on StackBlitz, which previews each
|
|
18
|
+
* port on its own hostname, so a page opened before collection ended waited for
|
|
19
|
+
* the list on a socket that never connected, and stayed blank (#1245). It is
|
|
20
|
+
* also how `@nuxt/vite-builder`'s 24678 default made two books collide (#221).
|
|
16
21
|
*/
|
|
17
|
-
export declare function
|
|
22
|
+
export declare function socketOnBookPort(server: {
|
|
23
|
+
hmr?: unknown;
|
|
24
|
+
ws?: unknown;
|
|
25
|
+
}): {
|
|
26
|
+
hmr: SocketOptions;
|
|
27
|
+
ws: SocketOptions;
|
|
28
|
+
};
|
|
29
|
+
export {};
|
|
@@ -22,14 +22,24 @@ export function resolvePort(value, command) {
|
|
|
22
22
|
}
|
|
23
23
|
return port;
|
|
24
24
|
}
|
|
25
|
+
function withoutOwnPort(options) {
|
|
26
|
+
if (options === null || typeof options !== 'object') {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
const { port: _port, server: _server, ...rest } = options;
|
|
30
|
+
return rest;
|
|
31
|
+
}
|
|
25
32
|
/**
|
|
26
|
-
* The HMR socket
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
33
|
+
* The book server's HMR socket settings: whatever the merged config asked for,
|
|
34
|
+
* minus a port or server of its own, so Vite serves the socket on the book's port
|
|
35
|
+
* and the browser dials the page's own origin. Vite takes these from `server.ws`
|
|
36
|
+
* and from the deprecated `server.hmr` alike, so both are cleared.
|
|
37
|
+
*
|
|
38
|
+
* A socket on a second port was never routed on StackBlitz, which previews each
|
|
39
|
+
* port on its own hostname, so a page opened before collection ended waited for
|
|
40
|
+
* the list on a socket that never connected, and stayed blank (#1245). It is
|
|
41
|
+
* also how `@nuxt/vite-builder`'s 24678 default made two books collide (#221).
|
|
31
42
|
*/
|
|
32
|
-
export function
|
|
33
|
-
|
|
34
|
-
return base < 45536 ? base + 20000 : base - 20000;
|
|
43
|
+
export function socketOnBookPort(server) {
|
|
44
|
+
return { hmr: withoutOwnPort(server.hmr), ws: withoutOwnPort(server.ws) };
|
|
35
45
|
}
|
|
@@ -7,7 +7,11 @@ export async function previewCommand(options) {
|
|
|
7
7
|
// run — so the teardown has to span the command, not one phase of it (#434).
|
|
8
8
|
try {
|
|
9
9
|
const port = resolvePort(options.port, 'preview');
|
|
10
|
+
// The same file the book was built with: `outDir` is read from it, and
|
|
11
|
+
// without it a book built with `--config` was looked for in the default
|
|
12
|
+
// directory and every path answered 404 (#1302).
|
|
10
13
|
const ctx = await createContext({
|
|
14
|
+
configFile: options.config,
|
|
11
15
|
mode: 'build',
|
|
12
16
|
});
|
|
13
17
|
for (const plugin of ctx.config.plugins) {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* model the whole surface. Only options whose wrong type fails *inside someone
|
|
12
12
|
* else's code* are listed — the rest fail legibly on their own.
|
|
13
13
|
*/
|
|
14
|
-
export type ConfigKind = 'string' | 'boolean' | 'number' | 'string[]' | 'array' | 'object' | 'function';
|
|
14
|
+
export type ConfigKind = 'string' | 'boolean' | 'number' | 'string[]' | 'array' | 'object' | 'function' | 'null';
|
|
15
15
|
/** What the value actually is, in the words the message uses. */
|
|
16
16
|
export declare function describeKind(value: unknown): string;
|
|
17
17
|
export declare function configProblems(config: unknown, configFile: string): string[];
|
|
@@ -11,6 +11,13 @@ const RULES = [
|
|
|
11
11
|
{ path: 'defaultBackgroundColor', kind: 'string' },
|
|
12
12
|
{ path: 'sandboxDarkClass', kind: 'string' },
|
|
13
13
|
{ path: 'responsivePresets', kind: 'array' },
|
|
14
|
+
{ path: 'preview', kind: 'object' },
|
|
15
|
+
{ path: 'preview.backgroundColor', kind: 'string' },
|
|
16
|
+
{ path: 'preview.responsiveWidth', kind: ['number', 'null'] },
|
|
17
|
+
{ path: 'preview.responsiveHeight', kind: ['number', 'null'] },
|
|
18
|
+
{ path: 'preview.rotate', kind: 'boolean' },
|
|
19
|
+
{ path: 'preview.checkerboard', kind: 'boolean' },
|
|
20
|
+
{ path: 'preview.textDirection', kind: 'string' },
|
|
14
21
|
{ path: 'backgroundPresets', kind: 'array' },
|
|
15
22
|
{ path: 'theme.title', kind: 'string' },
|
|
16
23
|
{ path: 'theme.lang', kind: 'string' },
|
|
@@ -26,6 +33,9 @@ const RULES = [
|
|
|
26
33
|
];
|
|
27
34
|
/** What the value actually is, in the words the message uses. */
|
|
28
35
|
export function describeKind(value) {
|
|
36
|
+
if (value === null) {
|
|
37
|
+
return 'null';
|
|
38
|
+
}
|
|
29
39
|
if (Array.isArray(value)) {
|
|
30
40
|
return value.every(item => typeof item === 'string') ? 'string[]' : 'array';
|
|
31
41
|
}
|
|
@@ -47,6 +57,8 @@ function matches(value, kind) {
|
|
|
47
57
|
return typeof value === 'number';
|
|
48
58
|
case 'function':
|
|
49
59
|
return typeof value === 'function';
|
|
60
|
+
case 'null':
|
|
61
|
+
return value === null;
|
|
50
62
|
default:
|
|
51
63
|
return false;
|
|
52
64
|
}
|
package/dist/node/config.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export declare function loadConfigFile(configFile: string): Promise<Partial<Pove
|
|
|
7
7
|
export declare const mergeBuildConfig: import("defu").DefuFn;
|
|
8
8
|
export declare const mergeConfig: import("defu").DefuFn;
|
|
9
9
|
export declare function resolveConfig(cwd: string | undefined, mode: ConfigMode, configFile?: string): Promise<PovesteConfig>;
|
|
10
|
+
export declare function processDefaultConfig(defaultConfig: PovesteConfig, preUserConfig: Partial<PovesteConfig>, mode: ConfigMode, _cwd: string): Promise<PovesteConfig>;
|
|
10
11
|
export declare function processConfig(ctx: Context): Promise<void>;
|
|
11
12
|
export declare function defineConfig(config: Partial<PovesteConfig>): Partial<PovesteConfig>;
|
|
12
13
|
declare module 'vite' {
|
package/dist/node/config.js
CHANGED
|
@@ -325,13 +325,18 @@ async function resolveConfigPlugins(config, mode) {
|
|
|
325
325
|
}
|
|
326
326
|
return config;
|
|
327
327
|
}
|
|
328
|
-
async function processDefaultConfig(defaultConfig, preUserConfig, mode, _cwd) {
|
|
328
|
+
export async function processDefaultConfig(defaultConfig, preUserConfig, mode, _cwd) {
|
|
329
329
|
// Apply plugins
|
|
330
330
|
for (const plugin of [...defaultConfig.plugins, ...preUserConfig.plugins ?? []]) {
|
|
331
331
|
if (plugin.defaultConfig) {
|
|
332
332
|
const result = await plugin.defaultConfig(defaultConfig, mode);
|
|
333
333
|
if (result) {
|
|
334
|
-
|
|
334
|
+
// A plugin's story globs add to the defaults: a Solid plugin declaring
|
|
335
|
+
// `.story.tsx` must not drop `.story.vue` from a book that has both. Only
|
|
336
|
+
// here. A user's `storyMatch` still replaces, since narrowing is a
|
|
337
|
+
// legitimate use (#1124).
|
|
338
|
+
const storyMatch = result.storyMatch && [...new Set([...defaultConfig.storyMatch, ...result.storyMatch])];
|
|
339
|
+
defaultConfig = mergeOnto(storyMatch ? { ...result, storyMatch } : result, defaultConfig);
|
|
335
340
|
}
|
|
336
341
|
}
|
|
337
342
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Plugin } from '@poveste/shared';
|
|
2
|
+
/**
|
|
3
|
+
* Asks each plugin with an `onDevEvent` in turn, and returns the first answer
|
|
4
|
+
* to an event that wants one. An `on…` event goes to every plugin and answers
|
|
5
|
+
* nothing.
|
|
6
|
+
*
|
|
7
|
+
* A plugin that throws is logged under its name and skipped. Left to the
|
|
8
|
+
* listener, the rejection went unhandled and exited `poveste dev` (#1170).
|
|
9
|
+
*/
|
|
10
|
+
export declare function answerDevEvent(plugins: Plugin[], event: string, ask: (plugin: Plugin) => unknown): Promise<unknown>;
|
|
11
|
+
export interface DevCommandResult {
|
|
12
|
+
id: string;
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Runs a command's server half and always reports back. Left to the listener, a
|
|
17
|
+
* `serverAction` that threw (a story file that already exists, a path with no
|
|
18
|
+
* directory) went unhandled and exited `poveste dev` (#1176).
|
|
19
|
+
*/
|
|
20
|
+
export declare function runDevCommand(commands: {
|
|
21
|
+
id: string;
|
|
22
|
+
serverAction?: (params: any) => unknown;
|
|
23
|
+
}[], id: string, params: unknown): Promise<DevCommandResult>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import pc from 'picocolors';
|
|
2
|
+
/**
|
|
3
|
+
* Asks each plugin with an `onDevEvent` in turn, and returns the first answer
|
|
4
|
+
* to an event that wants one. An `on…` event goes to every plugin and answers
|
|
5
|
+
* nothing.
|
|
6
|
+
*
|
|
7
|
+
* A plugin that throws is logged under its name and skipped. Left to the
|
|
8
|
+
* listener, the rejection went unhandled and exited `poveste dev` (#1170).
|
|
9
|
+
*/
|
|
10
|
+
export async function answerDevEvent(plugins, event, ask) {
|
|
11
|
+
for (const plugin of plugins) {
|
|
12
|
+
if (!plugin.onDevEvent) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const result = await ask(plugin);
|
|
17
|
+
if (!event.startsWith('on') && result !== undefined) {
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
console.error(pc.red(`[Plugin:${plugin.name}]`), `onDevEvent threw on "${event}":`, error);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Runs a command's server half and always reports back. Left to the listener, a
|
|
29
|
+
* `serverAction` that threw (a story file that already exists, a path with no
|
|
30
|
+
* directory) went unhandled and exited `poveste dev` (#1176).
|
|
31
|
+
*/
|
|
32
|
+
export async function runDevCommand(commands, id, params) {
|
|
33
|
+
const command = commands.find(c => c.id === id);
|
|
34
|
+
try {
|
|
35
|
+
await command?.serverAction?.(params);
|
|
36
|
+
return { id };
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
console.error(pc.red(`[poveste] command "${id}" failed:`), error);
|
|
40
|
+
return { id, error: error instanceof Error ? error.message : String(error) };
|
|
41
|
+
}
|
|
42
|
+
}
|
package/dist/node/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { onPovesteCleanup } from './cleanup.js';
|
|
2
2
|
export { defaultColors } from './colors.js';
|
|
3
|
-
export
|
|
3
|
+
export { configFileNames, defineConfig, getDefaultConfig, loadConfigFile, mergeBuildConfig, mergeConfig, processConfig, resolveConfig, resolveConfigFile, } from './config.js';
|
|
4
4
|
export * from './plugin.js';
|
|
5
5
|
export { getInjectedImport } from './util/vendors.js';
|
|
6
6
|
export * from '@poveste/shared';
|
package/dist/node/index.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
// vendored copies of Vue and the controls from inside that generated code.
|
|
3
3
|
export { onPovesteCleanup } from './cleanup.js';
|
|
4
4
|
export { defaultColors } from './colors.js';
|
|
5
|
-
export
|
|
5
|
+
// Named, not `export *`: an `export` added to config.ts so a spec can reach a
|
|
6
|
+
// helper would otherwise publish it (#1291).
|
|
7
|
+
export { configFileNames, defineConfig, getDefaultConfig, loadConfigFile, mergeBuildConfig, mergeConfig, processConfig, resolveConfig, resolveConfigFile, } from './config.js';
|
|
6
8
|
export * from './plugin.js';
|
|
7
9
|
export { getInjectedImport } from './util/vendors.js';
|
|
8
10
|
export * from '@poveste/shared';
|
package/dist/node/server.js
CHANGED
|
@@ -2,7 +2,8 @@ import { performance } from 'node:perf_hooks';
|
|
|
2
2
|
import pc from 'picocolors';
|
|
3
3
|
import { createServer as createViteServer, mergeConfig as mergeViteConfig } from 'vite';
|
|
4
4
|
import { useCollectStories } from './collect/index.js';
|
|
5
|
-
import {
|
|
5
|
+
import { socketOnBookPort } from './commands/port.js';
|
|
6
|
+
import { answerDevEvent } from './dev-event.js';
|
|
6
7
|
import { useModuleLoader } from './load.js';
|
|
7
8
|
import { createMarkdownFilesWatcher, onMarkdownFileChange, onMarkdownListChange } from './markdown.js';
|
|
8
9
|
import { DevEventPluginApi, DevPluginApi } from './plugin.js';
|
|
@@ -42,14 +43,19 @@ async function startServer(ctx, options, onOpen, close) {
|
|
|
42
43
|
serverConfig.hmr = false;
|
|
43
44
|
}
|
|
44
45
|
else {
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
// On the book's own port, never a second one: StackBlitz does not route
|
|
47
|
+
// it, and `@nuxt/vite-builder`'s default collides between books (#1245,
|
|
48
|
+
// #221). Post-ordered, because that default is filled in by Nuxt's own
|
|
49
|
+
// `config` hook.
|
|
50
|
+
viteConfig.plugins = [...viteConfig.plugins ?? [], {
|
|
51
|
+
name: 'poveste:socket-on-book-port',
|
|
52
|
+
config: {
|
|
53
|
+
order: 'post',
|
|
54
|
+
handler(config) {
|
|
55
|
+
config.server = { ...config.server, ...socketOnBookPort(config.server ?? {}) };
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
}];
|
|
53
59
|
if (options.open) {
|
|
54
60
|
serverConfig.open = true;
|
|
55
61
|
}
|
|
@@ -97,15 +103,10 @@ async function startServer(ctx, options, onOpen, close) {
|
|
|
97
103
|
}
|
|
98
104
|
// Custom dev events
|
|
99
105
|
server.ws.on(`poveste:dev-event`, async ({ event, payload }) => {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (!event.startsWith('on') && result !== undefined) {
|
|
105
|
-
server.ws.send(`poveste:dev-event-result`, { event, result });
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
106
|
+
const result = await answerDevEvent(ctx.config.plugins, event, plugin => plugin.onDevEvent?.(new DevEventPluginApi(ctx, plugin, moduleLoader, event, payload, watches)));
|
|
107
|
+
// Sent even when nothing answered, so the client's promise settles.
|
|
108
|
+
if (!event.startsWith('on')) {
|
|
109
|
+
server.ws.send(`poveste:dev-event-result`, { event, result });
|
|
109
110
|
}
|
|
110
111
|
});
|
|
111
112
|
// Wait for pre-bundling (in `listen()`)
|
package/dist/node/vendors.d.ts
CHANGED
|
@@ -37,8 +37,14 @@ export declare function vendoredAliases(dependencies: Record<string, string> | u
|
|
|
37
37
|
* is the point: `poveste` has no `semver` dependency, a patch release is the wrong
|
|
38
38
|
* place to add one, and a hand-rolled `satisfies` that silently mis-handles a form it
|
|
39
39
|
* was never given is the failure this repo keeps producing. So `^X.Y.Z` with a major
|
|
40
|
-
* of at least 1 is answered, and `^0.x`, a
|
|
41
|
-
*
|
|
40
|
+
* of at least 1 is answered, and `^0.x`, a comparator set or anything else returns
|
|
41
|
+
* false and leaves the chrome on the copy it can certainly run.
|
|
42
|
+
*
|
|
43
|
+
* A prerelease is answered too, though npm's own caret leaves one out. Refusing it
|
|
44
|
+
* left the chrome on the vendored Vue while pinia's bare `vue` took the project's
|
|
45
|
+
* prerelease, which is #1060's split, and the book rendered no story (#1163). So a
|
|
46
|
+
* prerelease counts when its `X.Y.Z` is above the floor in the same major. One of the
|
|
47
|
+
* floor itself sorts below the floor, and is refused.
|
|
42
48
|
*/
|
|
43
49
|
export declare function satisfiesCaret(version: string | undefined, range: string | undefined): boolean;
|
|
44
50
|
/**
|
package/dist/node/vendors.js
CHANGED
|
@@ -69,17 +69,24 @@ export function vendoredAliases(dependencies) {
|
|
|
69
69
|
* is the point: `poveste` has no `semver` dependency, a patch release is the wrong
|
|
70
70
|
* place to add one, and a hand-rolled `satisfies` that silently mis-handles a form it
|
|
71
71
|
* was never given is the failure this repo keeps producing. So `^X.Y.Z` with a major
|
|
72
|
-
* of at least 1 is answered, and `^0.x`, a
|
|
73
|
-
*
|
|
72
|
+
* of at least 1 is answered, and `^0.x`, a comparator set or anything else returns
|
|
73
|
+
* false and leaves the chrome on the copy it can certainly run.
|
|
74
|
+
*
|
|
75
|
+
* A prerelease is answered too, though npm's own caret leaves one out. Refusing it
|
|
76
|
+
* left the chrome on the vendored Vue while pinia's bare `vue` took the project's
|
|
77
|
+
* prerelease, which is #1060's split, and the book rendered no story (#1163). So a
|
|
78
|
+
* prerelease counts when its `X.Y.Z` is above the floor in the same major. One of the
|
|
79
|
+
* floor itself sorts below the floor, and is refused.
|
|
74
80
|
*/
|
|
75
81
|
export function satisfiesCaret(version, range) {
|
|
76
82
|
const wanted = /^\^(\d+)\.(\d+)\.(\d+)$/.exec(range ?? '');
|
|
77
|
-
const have = /^(\d+)\.(\d+)\.(\d+)
|
|
83
|
+
const have = /^(\d+)\.(\d+)\.(\d+)(-[0-9A-Z.-]+)?$/i.exec(version ?? '');
|
|
78
84
|
if (!wanted || !have) {
|
|
79
85
|
return false;
|
|
80
86
|
}
|
|
81
87
|
const [wantedMajor, wantedMinor, wantedPatch] = wanted.slice(1).map(Number);
|
|
82
|
-
const [major, minor, patch] = have.slice(1).map(Number);
|
|
88
|
+
const [major, minor, patch] = have.slice(1, 4).map(Number);
|
|
89
|
+
const prerelease = have[4] !== undefined;
|
|
83
90
|
// `^0.x` narrows to the minor, and no vendored range uses it. Refuse rather than
|
|
84
91
|
// implement a rule nothing here exercises.
|
|
85
92
|
if (wantedMajor === 0 || wantedMajor === undefined || major === undefined) {
|
|
@@ -91,7 +98,7 @@ export function satisfiesCaret(version, range) {
|
|
|
91
98
|
if (minor !== wantedMinor) {
|
|
92
99
|
return minor > wantedMinor;
|
|
93
100
|
}
|
|
94
|
-
return patch >= wantedPatch;
|
|
101
|
+
return prerelease ? patch > wantedPatch : patch >= wantedPatch;
|
|
95
102
|
}
|
|
96
103
|
/**
|
|
97
104
|
* Points the chrome's Vue at the project's, when they are two copies.
|
|
@@ -33,19 +33,37 @@ ${resolvedStories.map((file, index) => {
|
|
|
33
33
|
export let files = [${files.map(file => `{${JSON.stringify(file).slice(1, -1)}, component: Comp${file.index}, source: () => import(${jsString(`virtual:story-source:${file.story.id}`)})}`).join(',\n')}]
|
|
34
34
|
export let tree = ${JSON.stringify(makeTree(ctx.config, resolvedStories))}
|
|
35
35
|
export let generation = ${storiesGeneration.get(ctx) ?? 0}
|
|
36
|
-
|
|
36
|
+
// Every copy of this module on a page shares these. A page can hold two: each
|
|
37
|
+
// importer's URL is stamped when the server serves it, so a list that changes
|
|
38
|
+
// between two of them gives each its own copy, and the one evaluated last takes
|
|
39
|
+
// over the hot update. Kept per copy, the handlers stayed with the other one, and
|
|
40
|
+
// the page applied every update to a list nobody was watching (#1237).
|
|
41
|
+
const shared = import.meta.hot
|
|
42
|
+
? (import.meta.hot.data.shared ??= { handlers: [], generation: -Infinity, files, tree })
|
|
43
|
+
: { handlers: [], generation, files, tree }
|
|
44
|
+
if (generation > shared.generation) {
|
|
45
|
+
const announce = shared.generation !== -Infinity
|
|
46
|
+
Object.assign(shared, { generation, files, tree })
|
|
47
|
+
if (announce) {
|
|
48
|
+
shared.handlers.forEach(h => h(files, tree))
|
|
49
|
+
}
|
|
50
|
+
}
|
|
37
51
|
export function onUpdate (cb) {
|
|
38
|
-
handlers.push(cb)
|
|
52
|
+
shared.handlers.push(cb)
|
|
53
|
+
// Registered through a copy the page has already moved past.
|
|
54
|
+
if (shared.generation > generation) {
|
|
55
|
+
cb(shared.files, shared.tree)
|
|
56
|
+
}
|
|
39
57
|
}
|
|
40
58
|
if (import.meta.hot) {
|
|
41
59
|
import.meta.hot.accept(newModule => {
|
|
60
|
+
// Undefined when the new copy failed to load. The next update carries the list.
|
|
61
|
+
if (!newModule) {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
42
64
|
files = newModule.files
|
|
43
65
|
tree = newModule.tree
|
|
44
66
|
generation = newModule.generation
|
|
45
|
-
handlers.forEach(h => {
|
|
46
|
-
h(newModule.files, newModule.tree)
|
|
47
|
-
newModule.onUpdate(h)
|
|
48
|
-
})
|
|
49
67
|
})
|
|
50
68
|
}`;
|
|
51
69
|
}
|
package/dist/node/vite.js
CHANGED
|
@@ -2,8 +2,10 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { lookup as lookupMime } from 'mrmime';
|
|
4
4
|
import { dirname, join, relative } from 'pathe';
|
|
5
|
-
import { loadConfigFromFile as loadViteConfigFromFile, mergeConfig as mergeViteConfig, searchForWorkspaceRoot, } from 'vite';
|
|
5
|
+
import { createLogger, loadConfigFromFile as loadViteConfigFromFile, mergeConfig as mergeViteConfig, normalizePath, searchForWorkspaceRoot, } from 'vite';
|
|
6
6
|
import { APP_PATH, TEMP_PATH } from './alias.js';
|
|
7
|
+
import { collectionLogger } from './collect/logger.js';
|
|
8
|
+
import { runDevCommand } from './dev-event.js';
|
|
7
9
|
import { createMarkdownPlugins } from './markdown.js';
|
|
8
10
|
import { optimizeEntries } from './optimize-entries.js';
|
|
9
11
|
import { notifyStoryChange } from './stories.js';
|
|
@@ -146,6 +148,13 @@ export async function getViteConfigWithPlugins(isServer, ctx) {
|
|
|
146
148
|
}
|
|
147
149
|
plugins.push({
|
|
148
150
|
name: 'poveste-vite-plugin',
|
|
151
|
+
// Only the core knows which files a book's `storyMatch` makes stories, and a
|
|
152
|
+
// framework plugin may compile those differently from the components they
|
|
153
|
+
// import (#1164). Not on `this.meta`: a Rolldown build does not carry what
|
|
154
|
+
// `options()` sets there into `transform`.
|
|
155
|
+
api: {
|
|
156
|
+
isStoryFile: (id) => ctx.storyFiles.some(file => normalizePath(file.path) === id),
|
|
157
|
+
},
|
|
149
158
|
config(_, { command }) {
|
|
150
159
|
const collapsedVue = isServer || process.env['POVESTE_DEV'] ? {} : collapseVendoredVue({ root: ctx.root });
|
|
151
160
|
return {
|
|
@@ -375,11 +384,8 @@ export async function getViteConfigWithPlugins(isServer, ctx) {
|
|
|
375
384
|
plugins.push({
|
|
376
385
|
name: 'poveste:dev-commands',
|
|
377
386
|
configureServer(server) {
|
|
378
|
-
server.ws.on('poveste:dev-command', ({ id, params }) => {
|
|
379
|
-
|
|
380
|
-
if (command?.serverAction) {
|
|
381
|
-
command.serverAction(params);
|
|
382
|
-
}
|
|
387
|
+
server.ws.on('poveste:dev-command', async ({ id, params }) => {
|
|
388
|
+
server.ws.send('poveste:dev-command-result', await runDevCommand(ctx.registeredCommands, id, params));
|
|
383
389
|
});
|
|
384
390
|
},
|
|
385
391
|
});
|
|
@@ -447,6 +453,11 @@ export async function getViteConfigWithPlugins(isServer, ctx) {
|
|
|
447
453
|
configFile: false,
|
|
448
454
|
plugins,
|
|
449
455
|
});
|
|
456
|
+
if (isServer) {
|
|
457
|
+
viteConfig.customLogger = collectionLogger(viteConfig.customLogger ?? createLogger(viteConfig.logLevel, {
|
|
458
|
+
...viteConfig.clearScreen === undefined ? {} : { allowClearScreen: viteConfig.clearScreen },
|
|
459
|
+
}));
|
|
460
|
+
}
|
|
450
461
|
return {
|
|
451
462
|
viteConfig,
|
|
452
463
|
viteConfigFile: userViteConfigFile?.path ?? null,
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poveste",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
5
|
-
"description": "Interactive component playgrounds for Vue, Nuxt, Svelte, SvelteKit and Quasar, plus Solid (renderer only) — a drop-in fork of histoire, with Quasar support histoire does not ship.",
|
|
4
|
+
"version": "0.19.0",
|
|
5
|
+
"description": "Interactive component playgrounds for Vue, Nuxt, Svelte, SvelteKit and Quasar, plus Solid and React (renderer only) — a drop-in fork of histoire, with Quasar support histoire does not ship.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Sorin Gitlan",
|
|
8
8
|
"url": "https://x.com/50rayn"
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"sveltekit",
|
|
32
32
|
"solid",
|
|
33
33
|
"solidjs",
|
|
34
|
+
"react",
|
|
34
35
|
"cli"
|
|
35
36
|
],
|
|
36
37
|
"publishConfig": {
|
|
@@ -77,10 +78,10 @@
|
|
|
77
78
|
},
|
|
78
79
|
"dependencies": {
|
|
79
80
|
"@11ty/gray-matter": "^3.0.0",
|
|
80
|
-
"@poveste/app": "^0.
|
|
81
|
-
"@poveste/controls": "^0.
|
|
82
|
-
"@poveste/shared": "^0.
|
|
83
|
-
"@poveste/vendors": "^0.
|
|
81
|
+
"@poveste/app": "^0.19.0",
|
|
82
|
+
"@poveste/controls": "^0.19.0",
|
|
83
|
+
"@poveste/shared": "^0.19.0",
|
|
84
|
+
"@poveste/vendors": "^0.19.0",
|
|
84
85
|
"change-case": "^5.4.4",
|
|
85
86
|
"chokidar": "^5.0.0",
|
|
86
87
|
"defu": "^6.1.4",
|