poveste 0.16.1 → 0.16.2
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/dist/node/vendors.d.ts +74 -0
- package/dist/node/vendors.js +157 -0
- package/dist/node/vite.js +9 -0
- package/package.json +5 -5
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves `specifier` as Node would from `from`, to a real path, or `undefined`
|
|
3
|
+
* when nothing answers.
|
|
4
|
+
*/
|
|
5
|
+
export type Resolver = (specifier: string, from: string) => string | undefined;
|
|
6
|
+
export declare const nodeResolver: Resolver;
|
|
7
|
+
export interface VendoredAlias {
|
|
8
|
+
/** The bare package the alias points at, `vue`. */
|
|
9
|
+
name: string;
|
|
10
|
+
/** The range it is pinned to, `^3.5.26`, or `''` when the spec carries none. */
|
|
11
|
+
range: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* What each vendored dependency aliases, by the name it is installed under:
|
|
15
|
+
* `{ 'poveste-vue': { name: 'vue', range: '^3.5.26' } }` from
|
|
16
|
+
* `"poveste-vue": "npm:vue@^3.5.26"`.
|
|
17
|
+
*
|
|
18
|
+
* Derived from the manifest rather than listed, because the set is whatever
|
|
19
|
+
* `@poveste/vendors` declares and a list here would keep working while silently
|
|
20
|
+
* covering one name fewer. The range comes back with the name because the caller
|
|
21
|
+
* needs both and this is the only place the spec is parsed.
|
|
22
|
+
*/
|
|
23
|
+
export declare function vendoredAliases(dependencies: Record<string, string> | undefined): Record<string, VendoredAlias>;
|
|
24
|
+
/**
|
|
25
|
+
* Whether `version` satisfies a caret range, for the caret ranges `@poveste/vendors`
|
|
26
|
+
* actually declares and **nothing else**.
|
|
27
|
+
*
|
|
28
|
+
* A range this cannot read with certainty is refused rather than approximated. That
|
|
29
|
+
* is the point: `poveste` has no `semver` dependency, a patch release is the wrong
|
|
30
|
+
* place to add one, and a hand-rolled `satisfies` that silently mis-handles a form it
|
|
31
|
+
* was never given is the failure this repo keeps producing. So `^X.Y.Z` with a major
|
|
32
|
+
* of at least 1 is answered, and `^0.x`, a prerelease, a comparator set or anything
|
|
33
|
+
* else returns false and leaves the chrome on the copy it can certainly run.
|
|
34
|
+
*/
|
|
35
|
+
export declare function satisfiesCaret(version: string | undefined, range: string | undefined): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Points the chrome's Vue at the project's, when they are two copies.
|
|
38
|
+
*
|
|
39
|
+
* `@poveste/vendors` depends on `poveste-vue`, an npm alias of `vue`. An alias is
|
|
40
|
+
* a **distinct package name**, so a package manager installs a second physical copy
|
|
41
|
+
* whatever the project already has — the project's Vue satisfying the aliased range
|
|
42
|
+
* does not collapse them, and `resolve.dedupe` cannot, because it matches on name.
|
|
43
|
+
*
|
|
44
|
+
* Two copies is two reactivity systems. The chrome's components render with the
|
|
45
|
+
* vendored one while `RouterView` — reached through `poveste-vue-router`, whose own
|
|
46
|
+
* `vue` import is bare — reacts with the project's, so `RouterView`'s first render
|
|
47
|
+
* is never invalidated and a Vue consumer's `poveste dev` showed a sidebar, a
|
|
48
|
+
* breadcrumb and no story at all. Nothing warns: neither copy is wrong, they simply
|
|
49
|
+
* cannot see each other (#1060).
|
|
50
|
+
*
|
|
51
|
+
* Only Vue. The reactivity identity is what breaks, and `@poveste/plugin-vue`
|
|
52
|
+
* declaring `vue` as a peer is what licenses pointing the chrome at the project's
|
|
53
|
+
* copy — no peer range licenses the same for pinia, vue-router, VueUse or Iconify,
|
|
54
|
+
* and making the chrome's pinned copies authoritative for a project's own story code
|
|
55
|
+
* is a larger change than this defect asks for.
|
|
56
|
+
*
|
|
57
|
+
* Empty when the project has no Vue of its own, when the two already resolve to one
|
|
58
|
+
* file, or when the project's Vue does not satisfy the range vendors pins — in each
|
|
59
|
+
* case the vendored copy is what the chrome should keep. Refusing on anything it
|
|
60
|
+
* cannot read with certainty is deliberate: the failure of this function is a chrome
|
|
61
|
+
* that keeps a Vue it can certainly run, and the failure of guessing is a chrome
|
|
62
|
+
* handed one it cannot.
|
|
63
|
+
*
|
|
64
|
+
* The range is checked rather than assumed from the peer floor. A project only holds
|
|
65
|
+
* a Vue of its own because a framework plugin's peer range put it there — except that
|
|
66
|
+
* `--legacy-peer-deps` is exactly how a project ends up holding one the peer range
|
|
67
|
+
* would have refused (#1062), which leaves a reader on, say, Vue 3.2 with a chrome
|
|
68
|
+
* collapsed onto a Vue that cannot run it. Such a project is already broken without
|
|
69
|
+
* this fix, but "already broken" is not a reason to hand it a different break.
|
|
70
|
+
*/
|
|
71
|
+
export declare function collapseVendoredVue(options: {
|
|
72
|
+
root: string;
|
|
73
|
+
resolve?: Resolver;
|
|
74
|
+
}): Record<string, string>;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { readFileSync, realpathSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { join } from 'pathe';
|
|
4
|
+
export const nodeResolver = (specifier, from) => {
|
|
5
|
+
try {
|
|
6
|
+
// `createRequire` resolves relative to the *dirname* of what it is given, so a
|
|
7
|
+
// directory has to be named as a file inside it or every lookup starts one
|
|
8
|
+
// level too high.
|
|
9
|
+
return realpathSync(createRequire(join(from, 'noop.js')).resolve(specifier));
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* What each vendored dependency aliases, by the name it is installed under:
|
|
17
|
+
* `{ 'poveste-vue': { name: 'vue', range: '^3.5.26' } }` from
|
|
18
|
+
* `"poveste-vue": "npm:vue@^3.5.26"`.
|
|
19
|
+
*
|
|
20
|
+
* Derived from the manifest rather than listed, because the set is whatever
|
|
21
|
+
* `@poveste/vendors` declares and a list here would keep working while silently
|
|
22
|
+
* covering one name fewer. The range comes back with the name because the caller
|
|
23
|
+
* needs both and this is the only place the spec is parsed.
|
|
24
|
+
*/
|
|
25
|
+
export function vendoredAliases(dependencies) {
|
|
26
|
+
const aliases = {};
|
|
27
|
+
for (const [installedAs, spec] of Object.entries(dependencies ?? {})) {
|
|
28
|
+
if (!spec.startsWith('npm:')) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
// Split on the last `@` rather than matching a name pattern: a scoped package
|
|
32
|
+
// opens with one, so the separator is positional and a regex that allows both
|
|
33
|
+
// is the kind the linter rejects for backtracking.
|
|
34
|
+
const target = spec.slice('npm:'.length);
|
|
35
|
+
const at = target.lastIndexOf('@');
|
|
36
|
+
aliases[installedAs] = at > 0
|
|
37
|
+
? { name: target.slice(0, at), range: target.slice(at + 1) }
|
|
38
|
+
: { name: target, range: '' };
|
|
39
|
+
}
|
|
40
|
+
return aliases;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Whether `version` satisfies a caret range, for the caret ranges `@poveste/vendors`
|
|
44
|
+
* actually declares and **nothing else**.
|
|
45
|
+
*
|
|
46
|
+
* A range this cannot read with certainty is refused rather than approximated. That
|
|
47
|
+
* is the point: `poveste` has no `semver` dependency, a patch release is the wrong
|
|
48
|
+
* place to add one, and a hand-rolled `satisfies` that silently mis-handles a form it
|
|
49
|
+
* was never given is the failure this repo keeps producing. So `^X.Y.Z` with a major
|
|
50
|
+
* of at least 1 is answered, and `^0.x`, a prerelease, a comparator set or anything
|
|
51
|
+
* else returns false and leaves the chrome on the copy it can certainly run.
|
|
52
|
+
*/
|
|
53
|
+
export function satisfiesCaret(version, range) {
|
|
54
|
+
const wanted = /^\^(\d+)\.(\d+)\.(\d+)$/.exec(range ?? '');
|
|
55
|
+
const have = /^(\d+)\.(\d+)\.(\d+)$/.exec(version ?? '');
|
|
56
|
+
if (!wanted || !have) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
const [wantedMajor, wantedMinor, wantedPatch] = wanted.slice(1).map(Number);
|
|
60
|
+
const [major, minor, patch] = have.slice(1).map(Number);
|
|
61
|
+
// `^0.x` narrows to the minor, and no vendored range uses it. Refuse rather than
|
|
62
|
+
// implement a rule nothing here exercises.
|
|
63
|
+
if (wantedMajor === 0 || wantedMajor === undefined || major === undefined) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
if (major !== wantedMajor) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
if (minor !== wantedMinor) {
|
|
70
|
+
return minor > wantedMinor;
|
|
71
|
+
}
|
|
72
|
+
return patch >= wantedPatch;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Points the chrome's Vue at the project's, when they are two copies.
|
|
76
|
+
*
|
|
77
|
+
* `@poveste/vendors` depends on `poveste-vue`, an npm alias of `vue`. An alias is
|
|
78
|
+
* a **distinct package name**, so a package manager installs a second physical copy
|
|
79
|
+
* whatever the project already has — the project's Vue satisfying the aliased range
|
|
80
|
+
* does not collapse them, and `resolve.dedupe` cannot, because it matches on name.
|
|
81
|
+
*
|
|
82
|
+
* Two copies is two reactivity systems. The chrome's components render with the
|
|
83
|
+
* vendored one while `RouterView` — reached through `poveste-vue-router`, whose own
|
|
84
|
+
* `vue` import is bare — reacts with the project's, so `RouterView`'s first render
|
|
85
|
+
* is never invalidated and a Vue consumer's `poveste dev` showed a sidebar, a
|
|
86
|
+
* breadcrumb and no story at all. Nothing warns: neither copy is wrong, they simply
|
|
87
|
+
* cannot see each other (#1060).
|
|
88
|
+
*
|
|
89
|
+
* Only Vue. The reactivity identity is what breaks, and `@poveste/plugin-vue`
|
|
90
|
+
* declaring `vue` as a peer is what licenses pointing the chrome at the project's
|
|
91
|
+
* copy — no peer range licenses the same for pinia, vue-router, VueUse or Iconify,
|
|
92
|
+
* and making the chrome's pinned copies authoritative for a project's own story code
|
|
93
|
+
* is a larger change than this defect asks for.
|
|
94
|
+
*
|
|
95
|
+
* Empty when the project has no Vue of its own, when the two already resolve to one
|
|
96
|
+
* file, or when the project's Vue does not satisfy the range vendors pins — in each
|
|
97
|
+
* case the vendored copy is what the chrome should keep. Refusing on anything it
|
|
98
|
+
* cannot read with certainty is deliberate: the failure of this function is a chrome
|
|
99
|
+
* that keeps a Vue it can certainly run, and the failure of guessing is a chrome
|
|
100
|
+
* handed one it cannot.
|
|
101
|
+
*
|
|
102
|
+
* The range is checked rather than assumed from the peer floor. A project only holds
|
|
103
|
+
* a Vue of its own because a framework plugin's peer range put it there — except that
|
|
104
|
+
* `--legacy-peer-deps` is exactly how a project ends up holding one the peer range
|
|
105
|
+
* would have refused (#1062), which leaves a reader on, say, Vue 3.2 with a chrome
|
|
106
|
+
* collapsed onto a Vue that cannot run it. Such a project is already broken without
|
|
107
|
+
* this fix, but "already broken" is not a reason to hand it a different break.
|
|
108
|
+
*/
|
|
109
|
+
export function collapseVendoredVue(options) {
|
|
110
|
+
const { root } = options;
|
|
111
|
+
const resolve = options.resolve ?? nodeResolver;
|
|
112
|
+
// From the vendors directory, not from the project: that is where the import
|
|
113
|
+
// happens, and an isolated `node_modules` layout makes `poveste-vue` invisible
|
|
114
|
+
// from the project root while the chrome resolves it perfectly well.
|
|
115
|
+
// Through an exported subpath: vendors' `exports` map does not list
|
|
116
|
+
// `./package.json`, so resolving that name directly fails.
|
|
117
|
+
const vendorsEntry = resolve('@poveste/vendors/vue', root);
|
|
118
|
+
const vendorsDir = vendorsEntry ? join(vendorsEntry, '..') : undefined;
|
|
119
|
+
if (!vendorsDir) {
|
|
120
|
+
return {};
|
|
121
|
+
}
|
|
122
|
+
const vueAlias = Object.entries(vendoredAliases(readJson(join(vendorsDir, 'package.json'))?.dependencies))
|
|
123
|
+
.find(([, alias]) => alias.name === 'vue');
|
|
124
|
+
if (!vueAlias) {
|
|
125
|
+
return {};
|
|
126
|
+
}
|
|
127
|
+
const [installedAs, { range }] = vueAlias;
|
|
128
|
+
const vendored = resolve(installedAs, vendorsDir);
|
|
129
|
+
const project = resolve('vue', root);
|
|
130
|
+
if (!vendored || !project || vendored === project) {
|
|
131
|
+
return {};
|
|
132
|
+
}
|
|
133
|
+
if (!satisfiesCaret(versionOf('vue/package.json', resolve, root), range)) {
|
|
134
|
+
return {};
|
|
135
|
+
}
|
|
136
|
+
// To the bare name, not to `project`. A resolved path is whatever the `require`
|
|
137
|
+
// condition answers — for Vue that is a CJS shim, and handing the chrome that
|
|
138
|
+
// replaced a split reactivity system with a browser app that does not boot at all.
|
|
139
|
+
// The name lets Vite resolve its own entry and condition, and `resolve.dedupe`
|
|
140
|
+
// above then genuinely covers the result.
|
|
141
|
+
return { [installedAs]: 'vue' };
|
|
142
|
+
}
|
|
143
|
+
function versionOf(specifier, resolve, root) {
|
|
144
|
+
const manifest = resolve(specifier, root);
|
|
145
|
+
if (!manifest) {
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
return readJson(manifest)?.version;
|
|
149
|
+
}
|
|
150
|
+
function readJson(file) {
|
|
151
|
+
try {
|
|
152
|
+
return JSON.parse(readFileSync(file, 'utf8'));
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
}
|
package/dist/node/vite.js
CHANGED
|
@@ -11,6 +11,7 @@ import { notifyStoryChange } from './stories.js';
|
|
|
11
11
|
import { chromeCssScopePlugin, globalStylesPlugin, userCssScopePlugin, } from './style-isolation/index.js';
|
|
12
12
|
import { applyHeadTransform } from './util/head.js';
|
|
13
13
|
import { viteCommand, viteMode } from './util/vite-mode.js';
|
|
14
|
+
import { collapseVendoredVue } from './vendors.js';
|
|
14
15
|
import { createVirtualFilesPlugin } from './virtual/vite-plugin.js';
|
|
15
16
|
const require = createRequire(import.meta.url);
|
|
16
17
|
export async function mergePovesteViteConfig(viteConfig, ctx) {
|
|
@@ -153,6 +154,14 @@ export async function getViteConfigWithPlugins(isServer, ctx) {
|
|
|
153
154
|
],
|
|
154
155
|
alias: {
|
|
155
156
|
'poveste-style': join(APP_PATH, process.env['POVESTE_DEV'] ? 'app/style/main.pcss' : 'style.css'),
|
|
157
|
+
// `dedupe` above cannot reach this one: the chrome's Vue is installed
|
|
158
|
+
// under a different name, and dedupe matches on the name (#1060).
|
|
159
|
+
//
|
|
160
|
+
// Not under `POVESTE_DEV`. That path already aliases bare `vue` to
|
|
161
|
+
// `@poveste/vendors/vue`, so sending `poveste-vue` back to `vue` would
|
|
162
|
+
// point the vendored entry at itself — the empty re-export its own
|
|
163
|
+
// comment warns about.
|
|
164
|
+
...(isServer || process.env['POVESTE_DEV'] ? {} : collapseVendoredVue({ root: ctx.root })),
|
|
156
165
|
},
|
|
157
166
|
...(isServer
|
|
158
167
|
? {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poveste",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.16.
|
|
4
|
+
"version": "0.16.2",
|
|
5
5
|
"description": "Interactive component playgrounds for Vue, Nuxt, Svelte, SvelteKit and Quasar — a drop-in fork of histoire, with Quasar support histoire does not ship.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Sorin Gitlan",
|
|
@@ -74,10 +74,10 @@
|
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
76
|
"@11ty/gray-matter": "^3.0.0",
|
|
77
|
-
"@poveste/app": "^0.16.
|
|
78
|
-
"@poveste/controls": "^0.16.
|
|
79
|
-
"@poveste/shared": "^0.16.
|
|
80
|
-
"@poveste/vendors": "^0.16.
|
|
77
|
+
"@poveste/app": "^0.16.2",
|
|
78
|
+
"@poveste/controls": "^0.16.2",
|
|
79
|
+
"@poveste/shared": "^0.16.2",
|
|
80
|
+
"@poveste/vendors": "^0.16.2",
|
|
81
81
|
"change-case": "^5.4.4",
|
|
82
82
|
"chokidar": "^5.0.0",
|
|
83
83
|
"defu": "^6.1.4",
|