poveste 0.14.0 → 0.15.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 +4 -2
- package/dist/node/__tests__/dev-cleanup/poveste.config.d.ts +7 -0
- package/dist/node/__tests__/dev-cleanup/poveste.config.js +15 -0
- package/dist/node/alias.js +1 -1
- package/dist/node/bin.js +5 -5
- package/dist/node/build-counts.d.ts +31 -1
- package/dist/node/build-counts.js +53 -1
- package/dist/node/build-serialize.d.ts +3 -3
- package/dist/node/build.js +54 -26
- package/dist/node/builtin-plugins/vanilla-support/MountStory.js +9 -9
- package/dist/node/builtin-plugins/vanilla-support/RenderStory.d.ts +1 -1
- package/dist/node/builtin-plugins/vanilla-support/RenderStory.js +5 -5
- package/dist/node/builtin-plugins/vanilla-support/collect.js +2 -2
- package/dist/node/builtin-plugins/vanilla-support/types.d.ts +2 -2
- package/dist/node/builtin-plugins/vanilla-support/vanilla.story.js +4 -4
- package/dist/node/collect/index.js +41 -39
- package/dist/node/collect/module-server.d.ts +14 -0
- package/dist/node/collect/module-server.js +268 -0
- package/dist/node/collect/rpc.d.ts +6 -0
- package/dist/node/collect/rpc.js +51 -0
- package/dist/node/collect/runner.d.ts +4 -0
- package/dist/node/collect/runner.js +130 -0
- package/dist/node/collect/worker.js +22 -25
- package/dist/node/colors.d.ts +4 -4
- package/dist/node/colors.js +6 -6
- package/dist/node/commands/dev.d.ts +13 -1
- package/dist/node/commands/dev.js +106 -45
- package/dist/node/config.d.ts +2 -2
- package/dist/node/config.js +50 -20
- package/dist/node/context.d.ts +2 -2
- package/dist/node/context.js +9 -4
- package/dist/node/dom/env.js +11 -8
- package/dist/node/dom/util.js +1 -1
- package/dist/node/load.js +10 -13
- package/dist/node/markdown.d.ts +3 -4
- package/dist/node/markdown.js +85 -36
- package/dist/node/optimize-entries.js +2 -2
- package/dist/node/plugin.d.ts +13 -8
- package/dist/node/plugin.js +24 -7
- package/dist/node/preview.d.ts +3 -3
- package/dist/node/preview.js +2 -2
- package/dist/node/search.d.ts +8 -2
- package/dist/node/server.d.ts +9 -4
- package/dist/node/server.js +70 -30
- package/dist/node/stories.d.ts +5 -4
- package/dist/node/stories.js +10 -6
- package/dist/node/style-isolation/entry-css-merger.js +2 -3
- package/dist/node/style-isolation/global-styles.js +1 -0
- package/dist/node/style-isolation/scope-chrome.js +2 -2
- package/dist/node/tree.js +29 -21
- package/dist/node/util/find-up.d.ts +1 -1
- package/dist/node/util/managed-watches.d.ts +14 -0
- package/dist/node/util/managed-watches.js +39 -0
- package/dist/node/util/unregister.d.ts +2 -0
- package/dist/node/util/unregister.js +9 -0
- package/dist/node/util/vendors.js +1 -3
- package/dist/node/util/watch-ignore.js +3 -3
- package/dist/node/virtual/resolved-config.js +4 -5
- package/dist/node/virtual/resolved-generated-setup-code.d.ts +1 -1
- package/dist/node/virtual/resolved-generated-setup-code.js +1 -1
- package/dist/node/virtual/resolved-markdown-files.js +1 -1
- package/dist/node/virtual/resolved-support-plugins-client.js +1 -1
- package/dist/node/virtual/resolved-support-plugins-collect.js +1 -1
- package/dist/node/virtual/resolved-theme.js +8 -3
- package/dist/node/virtual/story-source.d.ts +1 -1
- package/dist/node/virtual/story-source.js +2 -1
- package/dist/node/virtual/story.d.ts +1 -1
- package/dist/node/virtual/story.js +1 -0
- package/dist/node/virtual/util.d.ts +1 -1
- package/dist/node/virtual/vite-plugin.js +2 -0
- package/dist/node/vite.d.ts +1 -1
- package/dist/node/vite.js +50 -12
- package/package.json +21 -23
package/README.md
CHANGED
|
@@ -20,6 +20,8 @@ pnpm i -D poveste @poveste/plugin-nuxt # Nuxt 4
|
|
|
20
20
|
pnpm i -D poveste @poveste/plugin-quasar # Quasar
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
> **Installed an older version than you expected?** For about a day after a release, pnpm installs the previous one — `(x.y.z is available)`, no error. That is pnpm's release-age cooldown, not a broken publish. Use `npm`, wait it out, or pass `--config.minimum-release-age=0` (kebab-case — pnpm 12 silently ignores the camelCase spelling). Asking for the exact version does not help: pnpm 12 refuses one inside the window too.
|
|
24
|
+
|
|
23
25
|
Then create a `poveste.config.ts` in your project root:
|
|
24
26
|
|
|
25
27
|
```ts
|
|
@@ -46,11 +48,11 @@ Run `npx poveste --help` for the full list of options.
|
|
|
46
48
|
## Requirements
|
|
47
49
|
|
|
48
50
|
Node `^22.22.2 || ^24.15.0 || >=26.0.0` and Vite `^8.0.0`. See
|
|
49
|
-
[supported versions](https://poveste.dev/guide/getting-started
|
|
51
|
+
[supported versions](https://poveste.dev/guide/getting-started#supported-versions) for
|
|
50
52
|
what proves each range.
|
|
51
53
|
|
|
52
54
|
## Links
|
|
53
55
|
|
|
54
56
|
- [Documentation](https://poveste.dev)
|
|
55
|
-
- [Migrating from histoire](https://poveste.dev/guide/migration-from-histoire
|
|
57
|
+
- [Migrating from histoire](https://poveste.dev/guide/migration-from-histoire)
|
|
56
58
|
- [GitHub](https://github.com/poveste-dev/poveste)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { appendFileSync } from 'node:fs';
|
|
2
|
+
import { onPovesteCleanup } from 'poveste';
|
|
3
|
+
const marker = process.env['POVESTE_CLEANUP_MARKER'] ?? '';
|
|
4
|
+
// Registers the way @poveste/plugin-nuxt does, while the config resolves, and
|
|
5
|
+
// leaves a line behind each time the cleanup runs.
|
|
6
|
+
// `.ts`, not `.js`: the restart specs edit this file, and a `.js` config can be
|
|
7
|
+
// served from Node's module cache, so the edit would never be read.
|
|
8
|
+
export default {
|
|
9
|
+
plugins: [{
|
|
10
|
+
name: 'cleanup-marker',
|
|
11
|
+
defaultConfig() {
|
|
12
|
+
onPovesteCleanup(() => appendFileSync(marker, 'ran\n'));
|
|
13
|
+
},
|
|
14
|
+
}],
|
|
15
|
+
};
|
package/dist/node/alias.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import path from 'pathe';
|
|
3
3
|
const require = createRequire(import.meta.url);
|
|
4
|
-
export const APP_PATH = path.join(path.dirname(require.resolve('@poveste/app/package.json')), process.env
|
|
4
|
+
export const APP_PATH = path.join(path.dirname(require.resolve('@poveste/app/package.json')), process.env['POVESTE_DEV'] ? 'src' : 'dist');
|
|
5
5
|
export const TEMP_PATH = path.join(process.cwd(), 'node_modules', '.poveste');
|
package/dist/node/bin.js
CHANGED
|
@@ -4,9 +4,9 @@ import { dirname, resolve } from 'pathe';
|
|
|
4
4
|
import sade from 'sade';
|
|
5
5
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
6
|
const { version } = JSON.parse(fs.readFileSync(resolve(__dirname, '../../package.json'), 'utf8'));
|
|
7
|
-
process.env
|
|
8
|
-
process.env
|
|
9
|
-
process.env
|
|
7
|
+
process.env['NODE_ENV'] = process.argv.includes('build') || process.argv.includes('preview') ? 'production' : 'development';
|
|
8
|
+
process.env['HISTOIRE'] = 'true';
|
|
9
|
+
process.env['POVESTE'] = 'true';
|
|
10
10
|
// sade does not await an action, so a rejected command was terminated only by
|
|
11
11
|
// Node's default handling of the unhandled rejection it became. That is not the
|
|
12
12
|
// CLI's to rely on: any dependency can install an `unhandledRejection` listener
|
|
@@ -15,7 +15,7 @@ process.env.POVESTE = 'true';
|
|
|
15
15
|
//
|
|
16
16
|
// So failure is reported here, the exit code set, and the process forced out if
|
|
17
17
|
// it does not leave on its own. Closing the collection ports (#426) was not
|
|
18
|
-
// enough on its own: a failed build still hangs in the
|
|
18
|
+
// enough on its own: a failed build still hangs in the nuxt and sveltekit books,
|
|
19
19
|
// holding `FSEventWrap` and `FSReqCallback` handles — watchers nothing closes on
|
|
20
20
|
// the error path. And `dev` and `preview` hold a listening server by design, so
|
|
21
21
|
// for them no amount of leak-fixing would ever let the loop drain.
|
|
@@ -47,7 +47,7 @@ process.env.POVESTE = 'true';
|
|
|
47
47
|
* that ends and one that does not.
|
|
48
48
|
*/
|
|
49
49
|
function proveItDrains() {
|
|
50
|
-
return !!process.env
|
|
50
|
+
return !!process.env['POVESTE_NO_FORCE_EXIT'];
|
|
51
51
|
}
|
|
52
52
|
function run(command) {
|
|
53
53
|
return command().then(() => undefined, (error) => {
|
|
@@ -15,7 +15,7 @@ export interface BuildCounts {
|
|
|
15
15
|
* "No variants" is not the question. A standalone `.story.md` is collected into a
|
|
16
16
|
* story with `docsOnly: true` and `variants: []` by construction (`markdown.ts`),
|
|
17
17
|
* so counting empty by variants alone reported every documentation page in the
|
|
18
|
-
* book as a problem — three of the four `examples/
|
|
18
|
+
* book as a problem — three of the four `examples/vue` warned about are
|
|
19
19
|
* `SHARED_STORY_TITLES` entries that a required spec fails if you delete them,
|
|
20
20
|
* which left the one real empty file hidden inside them (#670).
|
|
21
21
|
*
|
|
@@ -24,3 +24,33 @@ export interface BuildCounts {
|
|
|
24
24
|
* home page reading 57 Stories and 3 Documents disagrees with itself.
|
|
25
25
|
*/
|
|
26
26
|
export declare function buildCounts(storyFiles: ServerStoryFile[]): BuildCounts;
|
|
27
|
+
/**
|
|
28
|
+
* Whether the build produced anything a reader can open.
|
|
29
|
+
*
|
|
30
|
+
* Stated once because two callers ask it: the message below, and the colour
|
|
31
|
+
* `build.ts` prints that message in. Reading it back off the message instead —
|
|
32
|
+
* one line means green — ties the colour to the shape of an unrelated decision,
|
|
33
|
+
* and a second success line would turn a good build yellow.
|
|
34
|
+
*/
|
|
35
|
+
export declare function builtSomething(counts: BuildCounts): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* What a finished build says it produced.
|
|
38
|
+
*
|
|
39
|
+
* A separate function because the interesting case is a claim rather than a
|
|
40
|
+
* number: `✅ Built 0 stories (0 variants)` is true of a working build in a
|
|
41
|
+
* project with nothing to build, and it is what a first-time reader gets from
|
|
42
|
+
* the first command the guide tells them to run (#624). Nothing in it says
|
|
43
|
+
* where the build looked, so there is no next step in it either.
|
|
44
|
+
*
|
|
45
|
+
* Nothing built is two states, not one, and the likelier is the second: a
|
|
46
|
+
* reader who followed the guide has a story file and has not put a `<Story>`
|
|
47
|
+
* in it yet. Those files matched, so "nothing matched" is false of them, and
|
|
48
|
+
* "write a story file" asks for the one they already wrote.
|
|
49
|
+
*
|
|
50
|
+
* Not an error. A book with no stories is a valid thing to build — scaffolding
|
|
51
|
+
* is exactly that state — so this changes what is said, not the exit code.
|
|
52
|
+
*
|
|
53
|
+
* Documents count as content: a book of `.story.md` pages with no variants has
|
|
54
|
+
* something in it, and telling its author nothing was found would be wrong.
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildSummary(counts: BuildCounts, seconds: number, storyMatch: string[]): string[];
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* "No variants" is not the question. A standalone `.story.md` is collected into a
|
|
6
6
|
* story with `docsOnly: true` and `variants: []` by construction (`markdown.ts`),
|
|
7
7
|
* so counting empty by variants alone reported every documentation page in the
|
|
8
|
-
* book as a problem — three of the four `examples/
|
|
8
|
+
* book as a problem — three of the four `examples/vue` warned about are
|
|
9
9
|
* `SHARED_STORY_TITLES` entries that a required spec fails if you delete them,
|
|
10
10
|
* which left the one real empty file hidden inside them (#670).
|
|
11
11
|
*
|
|
@@ -29,3 +29,55 @@ export function buildCounts(storyFiles) {
|
|
|
29
29
|
}
|
|
30
30
|
return counts;
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Whether the build produced anything a reader can open.
|
|
34
|
+
*
|
|
35
|
+
* Stated once because two callers ask it: the message below, and the colour
|
|
36
|
+
* `build.ts` prints that message in. Reading it back off the message instead —
|
|
37
|
+
* one line means green — ties the colour to the shape of an unrelated decision,
|
|
38
|
+
* and a second success line would turn a good build yellow.
|
|
39
|
+
*/
|
|
40
|
+
export function builtSomething(counts) {
|
|
41
|
+
return counts.stories > 0 || counts.docs > 0;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* What a finished build says it produced.
|
|
45
|
+
*
|
|
46
|
+
* A separate function because the interesting case is a claim rather than a
|
|
47
|
+
* number: `✅ Built 0 stories (0 variants)` is true of a working build in a
|
|
48
|
+
* project with nothing to build, and it is what a first-time reader gets from
|
|
49
|
+
* the first command the guide tells them to run (#624). Nothing in it says
|
|
50
|
+
* where the build looked, so there is no next step in it either.
|
|
51
|
+
*
|
|
52
|
+
* Nothing built is two states, not one, and the likelier is the second: a
|
|
53
|
+
* reader who followed the guide has a story file and has not put a `<Story>`
|
|
54
|
+
* in it yet. Those files matched, so "nothing matched" is false of them, and
|
|
55
|
+
* "write a story file" asks for the one they already wrote.
|
|
56
|
+
*
|
|
57
|
+
* Not an error. A book with no stories is a valid thing to build — scaffolding
|
|
58
|
+
* is exactly that state — so this changes what is said, not the exit code.
|
|
59
|
+
*
|
|
60
|
+
* Documents count as content: a book of `.story.md` pages with no variants has
|
|
61
|
+
* something in it, and telling its author nothing was found would be wrong.
|
|
62
|
+
*/
|
|
63
|
+
export function buildSummary(counts, seconds, storyMatch) {
|
|
64
|
+
const took = `in ${seconds}s`;
|
|
65
|
+
const globs = storyMatch.join(', ');
|
|
66
|
+
if (!builtSomething(counts)) {
|
|
67
|
+
if (counts.empty.length) {
|
|
68
|
+
const files = `${counts.empty.length} file${counts.empty.length === 1 ? '' : 's'}`;
|
|
69
|
+
return [
|
|
70
|
+
`Built 0 stories ${took} — ${files} matched ${globs} and produced no story`,
|
|
71
|
+
'A story file needs a <Story> in it: https://poveste.dev/guide/',
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
return [
|
|
75
|
+
`Built 0 stories ${took} — nothing matched ${globs}`,
|
|
76
|
+
'Write a story file and run this again: https://poveste.dev/guide/',
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
const stories = `${counts.stories} stor${counts.stories === 1 ? 'y' : 'ies'}`;
|
|
80
|
+
const variants = `${counts.variants} variant${counts.variants === 1 ? '' : 's'}`;
|
|
81
|
+
const docs = counts.docs ? ` and ${counts.docs} document${counts.docs === 1 ? '' : 's'}` : '';
|
|
82
|
+
return [`✅ Built ${stories} (${variants})${docs} ${took}`];
|
|
83
|
+
}
|
|
@@ -3,9 +3,9 @@ import type { Context } from './context.js';
|
|
|
3
3
|
interface SerializedStory extends Omit<ServerStory, 'docsText'> {
|
|
4
4
|
relativePath: string;
|
|
5
5
|
supportPluginId: string;
|
|
6
|
-
treePath?: string[];
|
|
7
|
-
virtual?: boolean;
|
|
8
|
-
markdownFile
|
|
6
|
+
treePath?: string[] | undefined;
|
|
7
|
+
virtual?: boolean | undefined;
|
|
8
|
+
markdownFile: SerializedMarkdownFile | null;
|
|
9
9
|
}
|
|
10
10
|
interface SerializedMarkdownFile {
|
|
11
11
|
id: string;
|
package/dist/node/build.js
CHANGED
|
@@ -5,7 +5,7 @@ import { join } from 'pathe';
|
|
|
5
5
|
import pc from 'picocolors';
|
|
6
6
|
import { createServer as createViteServer, mergeConfig as mergeViteConfig, build as viteBuild, } from 'vite';
|
|
7
7
|
import { APP_PATH } from './alias.js';
|
|
8
|
-
import { buildCounts } from './build-counts.js';
|
|
8
|
+
import { buildCounts, buildSummary, builtSomething } from './build-counts.js';
|
|
9
9
|
import { getSerializedStoryData } from './build-serialize.js';
|
|
10
10
|
import { useCollectStories } from './collect/index.js';
|
|
11
11
|
import { useModuleLoader } from './load.js';
|
|
@@ -58,6 +58,17 @@ export async function build(ctx) {
|
|
|
58
58
|
// bundle built from it.
|
|
59
59
|
mode: viteMode(ctx.mode),
|
|
60
60
|
optimizeDeps: { include: [], noDiscovery: true },
|
|
61
|
+
plugins: [{
|
|
62
|
+
// Collection reads each file once, so this server needs no watcher, and
|
|
63
|
+
// one leaked from it: a transform still in flight when a failed build
|
|
64
|
+
// closes the server re-adds a file, Vite's chokidar reopens on `add`, and
|
|
65
|
+
// the native watcher that starts is never closed (#434). A `config` hook
|
|
66
|
+
// cannot say this, because merging a config skips `null`.
|
|
67
|
+
name: 'poveste:collect-without-watcher',
|
|
68
|
+
configResolved(config) {
|
|
69
|
+
Object.assign(config.server, { watch: null });
|
|
70
|
+
},
|
|
71
|
+
}],
|
|
61
72
|
}));
|
|
62
73
|
await server.pluginContainer.buildStart({});
|
|
63
74
|
// Cleanup a plugin registers runs however this ends. `onBuildEnd` cannot be that
|
|
@@ -106,7 +117,7 @@ export async function build(ctx) {
|
|
|
106
117
|
// Same wrapper the dev path uses for these exact calls (server.ts).
|
|
107
118
|
await wrapLogError('destroyCollectStories', () => destroyCollectStories());
|
|
108
119
|
}
|
|
109
|
-
const
|
|
120
|
+
const counts = buildCounts(ctx.storyFiles);
|
|
110
121
|
const { viteConfig: buildViteConfigRaw } = await getViteConfigWithPlugins(false, ctx);
|
|
111
122
|
const buildViteConfig = mergeViteConfig(buildViteConfigRaw, {
|
|
112
123
|
// Vite picks the `.env` file from `mode`, not from NODE_ENV — this said
|
|
@@ -114,7 +125,7 @@ export async function build(ctx) {
|
|
|
114
125
|
mode: viteMode(ctx.mode),
|
|
115
126
|
build: {
|
|
116
127
|
lib: false,
|
|
117
|
-
|
|
128
|
+
rolldownOptions: {
|
|
118
129
|
// Named, not an array. With an array the bundler derives `[name]` from the
|
|
119
130
|
// path, and `APP_PATH` is absolute — so any framework whose `entryFileNames`
|
|
120
131
|
// refuses a path-shaped `[name]` fails the build outright (#369, and
|
|
@@ -136,14 +147,15 @@ export async function build(ctx) {
|
|
|
136
147
|
},
|
|
137
148
|
},
|
|
138
149
|
});
|
|
150
|
+
const buildPlugins = buildViteConfig.plugins ??= [];
|
|
139
151
|
// For @vite/plugin-vue: Always put our vite server
|
|
140
152
|
// Disable template inlining
|
|
141
153
|
// (so that we no longer need defineExpose)
|
|
142
154
|
// Nuxt: replaces the Nuxt vite dev server
|
|
143
|
-
|
|
155
|
+
buildPlugins.push({
|
|
144
156
|
name: 'poveste-vue-plugin-override',
|
|
145
157
|
config(config) {
|
|
146
|
-
const vuePlugin = config.plugins
|
|
158
|
+
const vuePlugin = config.plugins?.find((p) => p.name === 'vite:vue');
|
|
147
159
|
if (vuePlugin) {
|
|
148
160
|
// @ts-expect-error vue plugin use function form
|
|
149
161
|
const original = vuePlugin.configureServer.bind(vuePlugin);
|
|
@@ -164,14 +176,16 @@ export async function build(ctx) {
|
|
|
164
176
|
}
|
|
165
177
|
},
|
|
166
178
|
});
|
|
167
|
-
|
|
179
|
+
buildPlugins.push({
|
|
168
180
|
name: 'poveste-build-config-override',
|
|
169
181
|
enforce: 'post',
|
|
170
182
|
config(config) {
|
|
183
|
+
const build = config.build ??= {};
|
|
184
|
+
const rolldownOptions = build.rolldownOptions ??= {};
|
|
171
185
|
// Don't externalize
|
|
172
|
-
|
|
186
|
+
rolldownOptions.external = [];
|
|
173
187
|
// Force chunk strategy
|
|
174
|
-
|
|
188
|
+
rolldownOptions.output = {
|
|
175
189
|
manualChunks(id) {
|
|
176
190
|
// Vite's runtime helpers (`\0vite/preload-helper.js` and friends) are
|
|
177
191
|
// virtual modules, so the node_modules routing below never sees them
|
|
@@ -196,6 +210,7 @@ export async function build(ctx) {
|
|
|
196
210
|
}
|
|
197
211
|
return 'vendor';
|
|
198
212
|
}
|
|
213
|
+
return undefined;
|
|
199
214
|
},
|
|
200
215
|
};
|
|
201
216
|
// A framework that declares Vite environments gives each one its own
|
|
@@ -204,73 +219,86 @@ export async function build(ctx) {
|
|
|
204
219
|
// while poveste kept looking in `outDir` (#369). Only the client environment
|
|
205
220
|
// builds the book. Where no framework declared environments there is nothing to
|
|
206
221
|
// override, and where one did this puts the book back where poveste reads it.
|
|
207
|
-
if (config.environments?.client?.build) {
|
|
208
|
-
config.environments
|
|
222
|
+
if (config.environments?.['client']?.build) {
|
|
223
|
+
config.environments['client'].build.outDir = ctx.config.outDir;
|
|
209
224
|
}
|
|
210
225
|
// Force vite build options
|
|
211
|
-
Object.assign(
|
|
226
|
+
Object.assign(build, {
|
|
212
227
|
outDir: ctx.config.outDir,
|
|
213
228
|
emptyOutDir: true,
|
|
214
229
|
// Re-merged per-entry by entry-css-merger.
|
|
215
230
|
cssCodeSplit: true,
|
|
216
231
|
// Not an oversight: the source pane prints story code by reading
|
|
217
232
|
// functions back at runtime, and `Function.prototype.toString()`
|
|
218
|
-
// returns whatever the minifier left — `codegen-
|
|
233
|
+
// returns whatever the minifier left — `codegen-vue.spec.ts` fails
|
|
219
234
|
// on three cases with this on (#329).
|
|
220
235
|
minify: false,
|
|
221
236
|
// Don't build in SSR mode
|
|
222
237
|
ssr: false,
|
|
223
238
|
});
|
|
224
|
-
config.define.__HST_COLLECT__
|
|
239
|
+
config.define = { ...config.define, __HST_COLLECT__: false };
|
|
225
240
|
},
|
|
226
241
|
});
|
|
227
242
|
const isolate = ctx.config.isolateStyles !== false;
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
243
|
+
buildPlugins.push(userCssScopePlugin({ enabled: isolate }));
|
|
244
|
+
buildPlugins.push(chromeCssScopePlugin({ enabled: isolate }));
|
|
245
|
+
buildPlugins.push(entryCssMergerPlugin({ isolateStyles: isolate }));
|
|
231
246
|
for (const cb of changeViteConfigCallbacks) {
|
|
232
|
-
console.log('vite config hook', cb);
|
|
233
247
|
await cb(buildViteConfig);
|
|
234
248
|
}
|
|
235
249
|
const results = await viteBuild(buildViteConfig);
|
|
236
250
|
const result = Array.isArray(results) ? results[0] : results;
|
|
237
|
-
|
|
238
|
-
|
|
251
|
+
if (!result) {
|
|
252
|
+
throw new Error('[poveste] the book build produced no output');
|
|
239
253
|
}
|
|
254
|
+
const findEntryCss = (entryName) => result.output.find(o => o.type === 'asset' && o.fileName === `${entryName}.css`);
|
|
240
255
|
const mainStyleOutput = findEntryCss('bundle-main')
|
|
241
|
-
?? result.output.find(o => o.
|
|
256
|
+
?? result.output.find(o => o.type === 'asset' && o.names.includes('style.css'));
|
|
242
257
|
const sandboxStyleOutput = findEntryCss('bundle-sandbox') ?? mainStyleOutput;
|
|
243
258
|
// Preload
|
|
244
|
-
const preloadOutputs = result.output.filter(o =>
|
|
259
|
+
const preloadOutputs = result.output.filter(o => o.type === 'chunk' && PRELOAD_MODULES.includes(o.name));
|
|
245
260
|
const preloadHtml = generateScriptLinks(preloadOutputs.map(o => o.fileName), 'preload', ctx);
|
|
246
261
|
// Prefetch
|
|
247
|
-
const prefetchOutputs = result.output.filter(o =>
|
|
262
|
+
const prefetchOutputs = result.output.filter(o => o.type === 'chunk' && PREFETCHED_MODULES.includes(o.name));
|
|
248
263
|
const prefetchHtml = generateScriptLinks(prefetchOutputs.map(o => o.fileName), 'prefetch', ctx);
|
|
249
264
|
// Index
|
|
250
|
-
const indexOutput = result.output.find(o => o.
|
|
265
|
+
const indexOutput = result.output.find(o => o.type === 'chunk' && o.name === 'bundle-main');
|
|
266
|
+
const sandboxOutput = result.output.find(o => o.type === 'chunk' && o.name === 'bundle-sandbox');
|
|
267
|
+
if (!indexOutput || !sandboxOutput || !mainStyleOutput || !sandboxStyleOutput) {
|
|
268
|
+
const missing = { 'bundle-main.js': indexOutput, 'bundle-sandbox.js': sandboxOutput, 'bundle-main.css': mainStyleOutput };
|
|
269
|
+
throw new Error(`[poveste] the book build produced no ${Object.entries(missing).filter(([, output]) => !output).map(([name]) => name).join(', ')}`);
|
|
270
|
+
}
|
|
251
271
|
let indexHtml = generateEntryHtml(indexOutput.fileName, mainStyleOutput.fileName, {
|
|
252
272
|
HEAD: `${preloadHtml}${prefetchHtml}`,
|
|
253
273
|
}, ctx);
|
|
254
274
|
indexHtml = await applyHeadTransform(indexHtml, ctx.config.head);
|
|
255
275
|
await writeFile('index.html', indexHtml, ctx);
|
|
256
276
|
// Sandbox
|
|
257
|
-
const sandboxOutput = result.output.find(o => o.name === 'bundle-sandbox' && o.type === 'chunk');
|
|
258
277
|
let sandboxHtml = generateEntryHtml(sandboxOutput.fileName, sandboxStyleOutput.fileName, {}, ctx);
|
|
259
278
|
sandboxHtml = await applyHeadTransform(sandboxHtml, ctx.config.head);
|
|
260
279
|
await writeFile('__sandbox.html', sandboxHtml, ctx);
|
|
261
280
|
await writeFile('poveste.json', JSON.stringify(getSerializedStoryData(ctx), null, 2), ctx);
|
|
262
281
|
const duration = performance.now() - startTime;
|
|
282
|
+
const { empty } = counts;
|
|
263
283
|
if (empty.length) {
|
|
264
284
|
console.warn(pc.yellow(`⚠️ ${empty.length} empty story file${empty.length === 1 ? '' : 's'}: ${empty.join(', ')}`));
|
|
265
285
|
}
|
|
266
|
-
|
|
267
|
-
|
|
286
|
+
// Green only when something was built. A tick over nothing is the claim
|
|
287
|
+
// #624 is about, and it is the first command the guide asks for.
|
|
288
|
+
const paint = builtSomething(counts) ? pc.green : pc.yellow;
|
|
289
|
+
for (const line of buildSummary(counts, Math.round(duration / 1000 * 100) / 100, ctx.config.storyMatch)) {
|
|
290
|
+
console.log(paint(line));
|
|
291
|
+
}
|
|
268
292
|
// Render
|
|
269
293
|
if (previewStoryCallbacks.length) {
|
|
270
294
|
const { baseUrl, close } = await startPreview({}, ctx);
|
|
271
295
|
try {
|
|
272
296
|
for (const storyFile of ctx.storyFiles) {
|
|
273
297
|
const story = storyFile.story;
|
|
298
|
+
// An empty story file collects no story, and there is nothing to render.
|
|
299
|
+
if (!story) {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
274
302
|
for (const variant of story.variants) {
|
|
275
303
|
const query = new URLSearchParams();
|
|
276
304
|
query.append('storyId', story.id);
|
|
@@ -8,7 +8,7 @@ export default _defineComponent({
|
|
|
8
8
|
},
|
|
9
9
|
},
|
|
10
10
|
setup(props) {
|
|
11
|
-
const options = props.story.file
|
|
11
|
+
const options = props.story.file?.component;
|
|
12
12
|
let rawVariants = [];
|
|
13
13
|
if (options.onMount) {
|
|
14
14
|
rawVariants = [{
|
|
@@ -19,16 +19,16 @@ export default _defineComponent({
|
|
|
19
19
|
}];
|
|
20
20
|
}
|
|
21
21
|
else {
|
|
22
|
-
rawVariants = options.variants;
|
|
22
|
+
rawVariants = options.variants ?? [];
|
|
23
23
|
}
|
|
24
|
-
for (const index
|
|
24
|
+
for (const [index, variant] of props.story.variants.entries()) {
|
|
25
25
|
const rawVariant = rawVariants[index];
|
|
26
|
-
Object.assign(
|
|
27
|
-
slots: () => ({ default: rawVariant
|
|
28
|
-
source: rawVariant
|
|
29
|
-
responsiveDisabled: rawVariant
|
|
30
|
-
autoPropsDisabled: rawVariant
|
|
31
|
-
setupApp: rawVariant
|
|
26
|
+
Object.assign(variant, {
|
|
27
|
+
slots: () => ({ default: rawVariant?.onMount, controls: rawVariant?.onMountControls }),
|
|
28
|
+
source: rawVariant?.source ?? options.source,
|
|
29
|
+
responsiveDisabled: rawVariant?.responsiveDisabled ?? options.responsiveDisabled,
|
|
30
|
+
autoPropsDisabled: rawVariant?.autoPropsDisabled ?? options.autoPropsDisabled,
|
|
31
|
+
setupApp: rawVariant?.setupApp ?? options.setupApp,
|
|
32
32
|
configReady: true,
|
|
33
33
|
});
|
|
34
34
|
}
|
|
@@ -14,7 +14,7 @@ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropT
|
|
|
14
14
|
default: string;
|
|
15
15
|
};
|
|
16
16
|
}>, {
|
|
17
|
-
sandbox: import("vue").Ref<HTMLDivElement, HTMLDivElement>;
|
|
17
|
+
sandbox: import("vue").Ref<HTMLDivElement | undefined, HTMLDivElement | undefined>;
|
|
18
18
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
19
19
|
ready: () => true;
|
|
20
20
|
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
@@ -26,7 +26,7 @@ export default _defineComponent({
|
|
|
26
26
|
setup(props, { emit }) {
|
|
27
27
|
const sandbox = _ref();
|
|
28
28
|
let mounting = false;
|
|
29
|
-
let app;
|
|
29
|
+
let app = null;
|
|
30
30
|
let appHooks;
|
|
31
31
|
async function unmountVariant() {
|
|
32
32
|
if (app) {
|
|
@@ -36,7 +36,7 @@ export default _defineComponent({
|
|
|
36
36
|
await hook();
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
app.el.
|
|
39
|
+
app.el.remove();
|
|
40
40
|
app = null;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
@@ -86,9 +86,9 @@ export default _defineComponent({
|
|
|
86
86
|
appHooks.onUnmount.push(cb);
|
|
87
87
|
},
|
|
88
88
|
};
|
|
89
|
-
const onMount = props.variant.slots()
|
|
90
|
-
await onMount(api);
|
|
91
|
-
sandbox.value
|
|
89
|
+
const onMount = Reflect.get(props.variant.slots?.() ?? {}, props.slotName);
|
|
90
|
+
await onMount?.(api);
|
|
91
|
+
sandbox.value?.appendChild(app.el);
|
|
92
92
|
emit('ready');
|
|
93
93
|
}
|
|
94
94
|
_onMounted(async () => {
|
|
@@ -10,7 +10,7 @@ export async function run({ file, storyData }) {
|
|
|
10
10
|
}];
|
|
11
11
|
}
|
|
12
12
|
else {
|
|
13
|
-
rawVariants = options.variants;
|
|
13
|
+
rawVariants = options.variants ?? [];
|
|
14
14
|
}
|
|
15
15
|
const story = {
|
|
16
16
|
id: options.id ?? file.id,
|
|
@@ -20,7 +20,7 @@ export async function run({ file, storyData }) {
|
|
|
20
20
|
icon: options.icon,
|
|
21
21
|
iconColor: options.iconColor,
|
|
22
22
|
docsOnly: options.docsOnly ?? false,
|
|
23
|
-
variants:
|
|
23
|
+
variants: [],
|
|
24
24
|
};
|
|
25
25
|
const variants = rawVariants.map((v, index) => ({
|
|
26
26
|
id: v.id ?? `${story.id}-${index}`,
|
|
@@ -12,8 +12,8 @@ export interface MountApi {
|
|
|
12
12
|
onUnmount: (cb: () => unknown) => Awaitable<void>;
|
|
13
13
|
}
|
|
14
14
|
export interface VanillaApi {
|
|
15
|
-
onMount?: (api: MountApi) => Awaitable<void
|
|
16
|
-
onMountControls?: (api: MountApi) => Awaitable<void
|
|
15
|
+
onMount?: ((api: MountApi) => Awaitable<void>) | undefined;
|
|
16
|
+
onMountControls?: ((api: MountApi) => Awaitable<void>) | undefined;
|
|
17
17
|
}
|
|
18
18
|
export type VanillaStorySetupHandler = (payload: {
|
|
19
19
|
app: App;
|
|
@@ -5,20 +5,20 @@ export default {
|
|
|
5
5
|
{
|
|
6
6
|
title: 'Hello',
|
|
7
7
|
onMount: ({ el, state, onUpdate }) => {
|
|
8
|
-
state
|
|
8
|
+
state['count'] = 0;
|
|
9
9
|
const div = document.createElement('div');
|
|
10
10
|
div.innerHTML = '<h1>Hello World</h1>';
|
|
11
11
|
el.appendChild(div);
|
|
12
12
|
const counter = document.createElement('pre');
|
|
13
|
-
counter.innerHTML = `Counter: ${state
|
|
13
|
+
counter.innerHTML = `Counter: ${state['counter']}`;
|
|
14
14
|
div.appendChild(counter);
|
|
15
15
|
onUpdate(() => {
|
|
16
|
-
counter.innerHTML = `Counter: ${state
|
|
16
|
+
counter.innerHTML = `Counter: ${state['count']}`;
|
|
17
17
|
});
|
|
18
18
|
},
|
|
19
19
|
onMountControls: ({ el, state, onUnmount }) => {
|
|
20
20
|
const add = () => {
|
|
21
|
-
state
|
|
21
|
+
state['count']++;
|
|
22
22
|
};
|
|
23
23
|
Object.assign(el.style, {
|
|
24
24
|
padding: '8px',
|