poveste 0.13.0 → 0.13.1

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.
@@ -1,6 +1,7 @@
1
1
  import { cpus } from 'node:os';
2
2
  import { MessageChannel } from 'node:worker_threads';
3
3
  import Tinypool from '@akryum/tinypool';
4
+ import { escapeRegExp } from '@poveste/shared';
4
5
  import { createBirpc } from 'birpc';
5
6
  import path, { relative } from 'pathe';
6
7
  import pc from 'picocolors';
@@ -29,7 +30,7 @@ export function useCollectStories(options, ctx) {
29
30
  // @TODO temporary fix for https://github.com/histoire-dev/histoire/issues/409
30
31
  /vite\w*\/dist\/client\/(client|env).mjs/,
31
32
  ...ctx.config.viteNodeInlineDeps ?? [],
32
- new RegExp(path.resolve(TEMP_PATH, 'plugins')),
33
+ new RegExp(escapeRegExp(path.resolve(TEMP_PATH, 'plugins'))),
33
34
  ],
34
35
  fallbackCJS: true,
35
36
  },
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Emitting values into generated modules.
3
+ *
4
+ * Every generator in this directory builds JavaScript source by interpolation,
5
+ * and most of the values come from somewhere the author of that source did not
6
+ * control — a plugin's id, a user's config key, the project's own path. Getting
7
+ * it wrong produces a syntax error inside a module nobody wrote and nobody can
8
+ * open, which is the symptom rather than a crash at the input (#609).
9
+ *
10
+ * There are two positions and they need different things.
11
+ */
12
+ /** A value in a string or key position. Valid JSON is valid JavaScript here. */
13
+ export declare function jsString(value: string): string;
14
+ /**
15
+ * A value in an *identifier* position — a binding name, an import name.
16
+ *
17
+ * Quoting cannot help here, so the only honest options are to reject the value
18
+ * or to rename it. These names are read back by consumers (`logos.square`,
19
+ * `generatedSetup.setupVue3`), so renaming would move the problem to whoever
20
+ * looks them up. It rejects, naming the value and where it came from.
21
+ */
22
+ export declare function jsIdentifier(value: string, what: string): string;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Emitting values into generated modules.
3
+ *
4
+ * Every generator in this directory builds JavaScript source by interpolation,
5
+ * and most of the values come from somewhere the author of that source did not
6
+ * control — a plugin's id, a user's config key, the project's own path. Getting
7
+ * it wrong produces a syntax error inside a module nobody wrote and nobody can
8
+ * open, which is the symptom rather than a crash at the input (#609).
9
+ *
10
+ * There are two positions and they need different things.
11
+ */
12
+ /** A value in a string or key position. Valid JSON is valid JavaScript here. */
13
+ export function jsString(value) {
14
+ return JSON.stringify(value);
15
+ }
16
+ /**
17
+ * A value in an *identifier* position — a binding name, an import name.
18
+ *
19
+ * Quoting cannot help here, so the only honest options are to reject the value
20
+ * or to rename it. These names are read back by consumers (`logos.square`,
21
+ * `generatedSetup.setupVue3`), so renaming would move the problem to whoever
22
+ * looks them up. It rejects, naming the value and where it came from.
23
+ */
24
+ export function jsIdentifier(value, what) {
25
+ if (!/^[A-Z_$][\w$]*$/i.test(value)) {
26
+ throw new Error(`${what} must be usable as a JavaScript identifier, and ${JSON.stringify(value)} is not. `
27
+ + 'It is emitted into a generated module as a binding name, where quoting cannot rescue it.');
28
+ }
29
+ return value;
30
+ }
@@ -1,3 +1,4 @@
1
+ import { jsIdentifier, jsString } from './codegen.js';
1
2
  const serializedFields = ['id', 'label', 'prompts', 'icon', 'searchText'];
2
3
  export function resolvedCommands(ctx) {
3
4
  const imports = [];
@@ -21,10 +22,10 @@ function getCommandFields(command, imports) {
21
22
  if (command.clientSetupFile) {
22
23
  const importedVar = `__setup${imports.length}__`;
23
24
  if (typeof command.clientSetupFile === 'string') {
24
- imports.push(`import ${importedVar} from '${command.clientSetupFile}'`);
25
+ imports.push(`import ${importedVar} from ${jsString(command.clientSetupFile)}`);
25
26
  }
26
27
  else {
27
- imports.push(`import { ${command.clientSetupFile.importName} as ${importedVar} } from '${command.clientSetupFile.file}'`);
28
+ imports.push(`import { ${jsIdentifier(command.clientSetupFile.importName, `clientSetupFile.importName '${command.clientSetupFile.importName}'`)} as ${importedVar} } from ${jsString(command.clientSetupFile.file)}`);
28
29
  }
29
30
  fields.push(`...${importedVar}`);
30
31
  }
@@ -1,10 +1,11 @@
1
+ import { jsIdentifier, jsString } from './codegen.js';
1
2
  export function resolvedConfig(ctx) {
2
3
  let js = `export const config = ${JSON.stringify(ctx.config)}\n`;
3
4
  if (ctx.config.theme?.logo) {
4
5
  for (const key in ctx.config.theme.logo) {
5
- js += `import Logo_${key} from '${ctx.config.theme.logo[key]}'\n`;
6
+ js += `import Logo_${jsIdentifier(key, `theme.logo key '${key}'`)} from ${jsString(ctx.config.theme.logo[key])}\n`;
6
7
  }
7
8
  }
8
- js += `export const logos = {${Object.keys(ctx.config.theme?.logo ?? {}).map(key => `${key}: Logo_${key}`).join(', ')}}\n`;
9
+ js += `export const logos = {${Object.keys(ctx.config.theme?.logo ?? {}).map(key => `${jsString(key)}: Logo_${key}`).join(', ')}}\n`;
9
10
  return js;
10
11
  }
@@ -1,4 +1,5 @@
1
1
  import { makeTree } from '../tree.js';
2
+ import { jsString } from './codegen.js';
2
3
  export function resolvedStories(ctx) {
3
4
  const resolvedStories = ctx.storyFiles.filter(s => !!s.story);
4
5
  const files = resolvedStories.map((file, index) => {
@@ -23,7 +24,7 @@ ${resolvedStories.map((file, index) => {
23
24
  }
24
25
  return supportPlugin.importStoryComponent(file, index);
25
26
  }).filter(Boolean).join('\n')}
26
- export let files = [${files.map(file => `{${JSON.stringify(file).slice(1, -1)}, component: Comp${file.index}, source: () => import('virtual:story-source:${file.story.id}')}`).join(',\n')}]
27
+ 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')}]
27
28
  export let tree = ${JSON.stringify(makeTree(ctx.config, resolvedStories))}
28
29
  const handlers = []
29
30
  export function onUpdate (cb) {
@@ -1,8 +1,9 @@
1
1
  import { createRequire } from 'node:module';
2
+ import { jsString } from './codegen.js';
2
3
  import { PLUGINS_HAVE_DEV } from './util.js';
3
4
  const require = createRequire(import.meta.url);
4
5
  export function resolvedSupportPluginsClient(ctx) {
5
- const plugins = ctx.supportPlugins.map(p => `'${p.id}': () => import(${JSON.stringify(require.resolve(`${p.moduleName}/client${process.env.POVESTE_DEV && PLUGINS_HAVE_DEV.includes(p.moduleName) ? '-dev' : ''}`, {
6
+ const plugins = ctx.supportPlugins.map(p => `${jsString(p.id)}: () => import(${JSON.stringify(require.resolve(`${p.moduleName}/client${process.env.POVESTE_DEV && PLUGINS_HAVE_DEV.includes(p.moduleName) ? '-dev' : ''}`, {
6
7
  paths: [ctx.root, import.meta.url],
7
8
  }))})`);
8
9
  return `export const clientSupportPlugins = {
@@ -1,8 +1,9 @@
1
1
  import { createRequire } from 'node:module';
2
+ import { jsString } from './codegen.js';
2
3
  import { PLUGINS_HAVE_DEV } from './util.js';
3
4
  const require = createRequire(import.meta.url);
4
5
  export function resolvedSupportPluginsCollect(ctx) {
5
- const plugins = ctx.supportPlugins.map(p => `'${p.id}': () => import(${JSON.stringify(require.resolve(`${p.moduleName}/collect${process.env.POVESTE_DEV && PLUGINS_HAVE_DEV.includes(p.moduleName) ? '-dev' : ''}`, {
6
+ const plugins = ctx.supportPlugins.map(p => `${jsString(p.id)}: () => import(${JSON.stringify(require.resolve(`${p.moduleName}/collect${process.env.POVESTE_DEV && PLUGINS_HAVE_DEV.includes(p.moduleName) ? '-dev' : ''}`, {
6
7
  paths: [ctx.root, import.meta.url],
7
8
  }))})`);
8
9
  return `export const collectSupportPlugins = {
@@ -1,3 +1,4 @@
1
+ import { jsIdentifier } from './codegen.js';
1
2
  export const ID_SEPARATOR = '__-__';
2
3
  /**
3
4
  * Names of every setup hook contributed by the enabled support plugins, e.g.
@@ -28,7 +29,7 @@ export function getSetupFnGroups(ctx) {
28
29
  * in every consumer build.
29
30
  */
30
31
  export function declareEmptySetupFns(ctx) {
31
- return getSetupFnNames(ctx).map(fnName => `export const ${fnName} = undefined`).join('\n');
32
+ return getSetupFnNames(ctx).map(fnName => `export const ${jsIdentifier(fnName, `support plugin setupFn '${fnName}'`)} = undefined`).join('\n');
32
33
  }
33
34
  export const PLUGINS_HAVE_DEV = [
34
35
  '@poveste/plugin-vue',
package/dist/node/vite.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { createRequire } from 'node:module';
3
+ import { escapeRegExp } from '@poveste/shared';
3
4
  import { lookup as lookupMime } from 'mrmime';
4
5
  import { dirname, join, relative } from 'pathe';
5
6
  import { loadConfigFromFile as loadViteConfigFromFile, mergeConfig as mergeViteConfig, searchForWorkspaceRoot, } from 'vite';
@@ -299,7 +300,7 @@ export async function getViteConfigWithPlugins(isServer, ctx) {
299
300
  if (/\.(?:vue|js)(?:$|\?)/.test(id)) {
300
301
  const original = code;
301
302
  for (const flag in flags) {
302
- code = code.replace(new RegExp(flag, 'g'), flags[flag]);
303
+ code = code.replace(new RegExp(escapeRegExp(flag), 'g'), flags[flag]);
303
304
  }
304
305
  if (original !== code)
305
306
  return code;
@@ -360,11 +361,11 @@ export async function getViteConfigWithPlugins(isServer, ctx) {
360
361
  ['vue', 'vue'],
361
362
  ].reduce((acc, [name, entry]) => {
362
363
  acc.push({
363
- find: new RegExp(`^${name.replace(/\//g, '\\/')}$`),
364
+ find: new RegExp(`^${escapeRegExp(name)}$`),
364
365
  replacement: `@poveste/vendors/${entry}`,
365
366
  });
366
367
  acc.push({
367
- find: new RegExp(`^${name.replace(/\//g, '\\/')}\\/`),
368
+ find: new RegExp(`^${escapeRegExp(name)}/`),
368
369
  replacement: `@poveste/vendors/${entry}/`,
369
370
  });
370
371
  return acc;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "poveste",
3
3
  "type": "module",
4
- "version": "0.13.0",
4
+ "version": "0.13.1",
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",
@@ -73,10 +73,10 @@
73
73
  },
74
74
  "dependencies": {
75
75
  "@akryum/tinypool": "^0.3.1",
76
- "@poveste/app": "^0.13.0",
77
- "@poveste/controls": "^0.13.0",
78
- "@poveste/shared": "^0.13.0",
79
- "@poveste/vendors": "^0.13.0",
76
+ "@poveste/app": "^0.13.1",
77
+ "@poveste/controls": "^0.13.1",
78
+ "@poveste/shared": "^0.13.1",
79
+ "@poveste/vendors": "^0.13.1",
80
80
  "@types/markdown-it": "^14.1.2",
81
81
  "birpc": "^0.2.19",
82
82
  "change-case": "^5.4.4",