@bakery-framework/plugin-vue 2.4.0 → 2.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bakery-framework/plugin-vue",
3
- "version": "2.4.0",
3
+ "version": "2.4.2",
4
4
  "description": "Bakery vue plugin.",
5
5
  "keywords": [
6
6
  "bakery",
@@ -43,7 +43,7 @@
43
43
  "vue": "^3.5.38"
44
44
  },
45
45
  "dependencies": {
46
- "@bakery-framework/core": "^2.4.0"
46
+ "@bakery-framework/core": "^2.4.2"
47
47
  },
48
48
  "engines": {
49
49
  "bun": ">=1.4.0"
package/src/compile.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { compileText } from '@bakery-framework/core/compiler'
2
+ import { Try } from '@bakery-framework/core/utils'
2
3
  import type { SFCStyleCompileResults } from '@vue/compiler-sfc'
3
4
 
4
5
  import type {
@@ -65,6 +66,33 @@ export function setVuePluginOptions(opts?: VuePluginOptions) {
65
66
  if (opts) vuePluginOptions = opts
66
67
  }
67
68
 
69
+ /**
70
+ * A test of whether an event attribute's value is one JavaScript expression,
71
+ * asked the way Vue's template compiler asks it: Babel over the value in
72
+ * parentheses, with the app's `expressionPlugins` and the `typescript` plugin
73
+ * Vue always adds.
74
+ *
75
+ * Vue compiles such a value as written. A function or a member path becomes
76
+ * the handler itself, and any other expression is wrapped as
77
+ * `$event => (value)`. `parseVueFile` asks before it appends a `;` to a
78
+ * multi-line attribute, because the `;` makes Vue compile the value as
79
+ * statements instead.
80
+ */
81
+ export async function loadExpressionCheck(): Promise<
82
+ (value: string) => boolean
83
+ > {
84
+ const { babelParse } = await loadCompiler()
85
+ const plugins = [
86
+ ...(vuePluginOptions.compilerOptions?.expressionPlugins ?? []),
87
+ 'typescript',
88
+ ]
89
+ return value =>
90
+ Try.return(() => {
91
+ const { body } = babelParse(`(${value})`, { plugins }).program
92
+ return body.length === 1 && body[0].type === 'ExpressionStatement'
93
+ }, false)
94
+ }
95
+
68
96
  /**
69
97
  * `'runtime'` unless the app opted into the full build.
70
98
  *
@@ -153,7 +181,7 @@ export async function compileScriptBlock(
153
181
  hoistStatic: true,
154
182
  })
155
183
  return {
156
- code: await compileText(result.content),
184
+ code: await compileText(result.content, { file: descriptor.filename }),
157
185
  bindings: result.bindings || {},
158
186
  }
159
187
  }
@@ -238,7 +266,7 @@ export async function compileTemplateBlock(
238
266
  // guarantee, so it is the one written down.
239
267
  let code = result.code
240
268
  code = code.replace(/^export\s+/m, '')
241
- code = await compileText(code)
269
+ code = await compileText(code, { file: filename })
242
270
  return { code, errors }
243
271
  }
244
272
 
package/src/handler.ts CHANGED
@@ -30,6 +30,7 @@ import { serveVueChunk, VUE_CHUNK_PREFIX } from './chunks'
30
30
  import {
31
31
  compileStyleBlock,
32
32
  compileVueFile,
33
+ loadExpressionCheck,
33
34
  parseVue,
34
35
  vueBuildVariant,
35
36
  } from './compile'
@@ -496,6 +497,19 @@ export class VueHandler extends DynamicHandler {
496
497
  }
497
498
  }
498
499
 
500
+ // A multi-line event attribute with no `;` gets one. Vue reads a value
501
+ // with no `;` as one expression, so two statements on two lines (as a
502
+ // formatter with `semi: false` writes them) came out wrapped in
503
+ // parentheses and failed to compile; with the `;`, Vue compiles the value
504
+ // as statements.
505
+ //
506
+ // Only when the value is not already one expression, which Vue compiles
507
+ // as written. With a `;` appended, an arrow handler broken across lines
508
+ // was emitted as `(d) => {…};` inside the render function's props and
509
+ // the module did not parse, a `function (e) {…}` failed Vue's own
510
+ // compile, and a member path (`saveDraft`) compiled to a statement that
511
+ // reads the function and never calls it.
512
+ const isExpression = await loadExpressionCheck()
499
513
  const rxEventAttr =
500
514
  /(@[\w.-]+|v-on:[\w.-]+)=("([^"\n]*\n[^"]*)"|'([^'\n]*\n[^']*)')/g
501
515
  cleanContent = cleanContent.replace(
@@ -503,7 +517,7 @@ export class VueHandler extends DynamicHandler {
503
517
  (match, attr, doubleMatch, innerDouble, innerSingle) => {
504
518
  const quote = doubleMatch.startsWith('"') ? '"' : "'"
505
519
  const val = innerDouble !== undefined ? innerDouble : innerSingle
506
- if (!val.includes(';')) {
520
+ if (!val.includes(';') && !isExpression(val)) {
507
521
  return `${attr}=${quote}${val};${quote}`
508
522
  }
509
523
  return match