@conduction/nextcloud-vue 2.2.0-vue3.13 → 2.2.0-vue3.14

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.
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Integration-leaf template-compile gate (Phase K / K1).
5
+ *
6
+ * THE NEAR-MISS THIS GUARDS AGAINST
7
+ * ---------------------------------
8
+ * During the pluggable-integration-registry rollout (ADR-019) a
9
+ * template-side ES2020 optional-chain slipped into
10
+ * `src/integrations/builtin/contacts/CnContactsCard.vue` — an
11
+ * `obj?.field` expression *inside the `<template>` block*. jest passed
12
+ * (its vue-jest transform tolerates modern syntax), but `npm run build`
13
+ * broke: Vue 2's render-function transpiler
14
+ * (vue-template-compiler → vue-template-es2015-compiler → buble) does
15
+ * NOT understand optional chaining (`?.`) or nullish coalescing (`??`)
16
+ * in template expressions. jest never traverses buble, so the failure
17
+ * only surfaced at build time.
18
+ *
19
+ * WHAT THIS DOES
20
+ * --------------
21
+ * Compiles every integration SFC's `<template>` block through the same
22
+ * `vue-template-compiler` that `rollup-plugin-vue` uses at build time,
23
+ * then runs the generated render function through buble — exactly the
24
+ * path that rejects template-side ES2020. A failure here is ATTRIBUTED
25
+ * to the offending integration file with a precise message, instead of
26
+ * surfacing as an opaque rollup error deep in `npm run build`.
27
+ *
28
+ * This is the fast-feedback companion to the full `npm run build`
29
+ * step in `.github/workflows/code-quality.yml`: the build remains the
30
+ * authoritative gate, but this script (wired into `npm run lint` peers
31
+ * via `check:integration-build` and the pre-commit hook) fails in
32
+ * seconds and points at the exact `.vue` file + line class.
33
+ *
34
+ * Scoped to `src/integrations/builtin/**.vue` to stay aligned with the
35
+ * K2 ESLint rule and avoid fleet-wide churn.
36
+ *
37
+ * Run via `npm run check:integration-build`.
38
+ *
39
+ * Exit codes:
40
+ * 0 — every integration SFC template compiles through buble
41
+ * 1 — at least one template uses syntax buble rejects (e.g. `?.`/`??`)
42
+ */
43
+
44
+ 'use strict'
45
+
46
+ const fs = require('fs')
47
+ const path = require('path')
48
+
49
+ const INTEGRATIONS_DIR = path.resolve(__dirname, '../src/integrations/builtin')
50
+
51
+ /**
52
+ * Recursively collect every `*.vue` file under a directory.
53
+ *
54
+ * @param {string} dir Directory to walk.
55
+ *
56
+ * @return {string[]} Absolute paths to `.vue` files.
57
+ */
58
+ function collectVueFiles(dir) {
59
+ const out = []
60
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
61
+ const full = path.join(dir, entry.name)
62
+ if (entry.isDirectory()) {
63
+ out.push(...collectVueFiles(full))
64
+ } else if (entry.isFile() && entry.name.endsWith('.vue')) {
65
+ out.push(full)
66
+ }
67
+ }
68
+ return out
69
+ }
70
+
71
+ /**
72
+ * Print the result to stdout/stderr.
73
+ *
74
+ * @param {Array<{file: string, message: string}>} list Failures.
75
+ *
76
+ * @return {void}
77
+ */
78
+ function report(list) {
79
+ if (list.length === 0) {
80
+ // eslint-disable-next-line no-console
81
+ console.log('✓ integration build: every integration SFC template compiles through buble (no template-side ES2020)')
82
+ return
83
+ }
84
+ // eslint-disable-next-line no-console
85
+ console.error('✗ integration template-compile gate failed:')
86
+ for (const f of list) {
87
+ // eslint-disable-next-line no-console
88
+ console.error(` - ${path.relative(process.cwd(), f.file)}: ${f.message}`)
89
+ }
90
+ // eslint-disable-next-line no-console
91
+ console.error('\nVue 2 template expressions are transpiled by buble, which does NOT')
92
+ // eslint-disable-next-line no-console
93
+ console.error('support optional chaining (?.) or nullish coalescing (??) inside a')
94
+ // eslint-disable-next-line no-console
95
+ console.error('<template> block. Move the expression into a computed/method, or use')
96
+ // eslint-disable-next-line no-console
97
+ console.error('an explicit (a && a.b) / (a == null ? d : a) form in the template.')
98
+ // eslint-disable-next-line no-console
99
+ console.error('(See K1/K2 of the integration-hardening change; ADR-019.)')
100
+ }
101
+
102
+ let vueCompiler
103
+ let transpileToFunctions
104
+ try {
105
+ // eslint-disable-next-line global-require, import/no-extraneous-dependencies
106
+ vueCompiler = require('vue-template-compiler')
107
+ // vue-template-compiler ships the es2015 (buble) transpiler that
108
+ // rollup-plugin-vue uses to turn the compiled render string into a
109
+ // function. This is the exact stage that rejects template ES2020.
110
+ // eslint-disable-next-line global-require, import/no-extraneous-dependencies
111
+ transpileToFunctions = require('vue-template-es2015-compiler')
112
+ } catch (e) {
113
+ // In a toolchain without these deps installed (e.g. a docs-only CI
114
+ // lane), fall back to a static scan of `<template>` blocks for `?.`
115
+ // and `??`. Coarser, but never silently passes.
116
+ staticFallback()
117
+ }
118
+
119
+ if (vueCompiler && transpileToFunctions) {
120
+ compileEach()
121
+ }
122
+
123
+ /**
124
+ * Authoritative path: compile each SFC template through
125
+ * vue-template-compiler + buble.
126
+ *
127
+ * @return {void}
128
+ */
129
+ function compileEach() {
130
+ const failures = []
131
+ for (const file of collectVueFiles(INTEGRATIONS_DIR)) {
132
+ const sfc = fs.readFileSync(file, 'utf8')
133
+ const parsed = vueCompiler.parseComponent(sfc)
134
+ const template = parsed.template && parsed.template.content
135
+ if (!template || template.trim() === '') {
136
+ continue
137
+ }
138
+ const compiled = vueCompiler.compile(template)
139
+ if (compiled.errors && compiled.errors.length > 0) {
140
+ failures.push({ file, message: compiled.errors.join('; ') })
141
+ continue
142
+ }
143
+ // The render fn string is what rollup-plugin-vue feeds to buble.
144
+ const code = `var render = function(){${compiled.render}}\n`
145
+ + `var staticRenderFns = [${(compiled.staticRenderFns || []).map(fn => `function(){${fn}}`).join(',')}]`
146
+ try {
147
+ transpileToFunctions(code, { transforms: { stripWithFunctional: false } })
148
+ } catch (err) {
149
+ failures.push({ file, message: `buble rejected template render fn (likely template-side ES2020 ?./??): ${err.message}` })
150
+ }
151
+ }
152
+ report(failures)
153
+ process.exit(failures.length === 0 ? 0 : 1)
154
+ }
155
+
156
+ /**
157
+ * Fallback path: static scan of `<template>` blocks for `?.` / `??`.
158
+ *
159
+ * @return {void}
160
+ */
161
+ function staticFallback() {
162
+ const failures = []
163
+ for (const file of collectVueFiles(INTEGRATIONS_DIR)) {
164
+ const sfc = fs.readFileSync(file, 'utf8')
165
+ const match = sfc.match(/<template[^>]*>([\s\S]*?)<\/template>/i)
166
+ if (!match) {
167
+ continue
168
+ }
169
+ const tpl = match[1]
170
+ // Optional chaining: `?.` not part of a ternary. Nullish: `??`.
171
+ if (/\?\./.test(tpl)) {
172
+ failures.push({ file, message: 'optional chaining (?.) found in <template> (buble rejects it)' })
173
+ }
174
+ if (/\?\?/.test(tpl)) {
175
+ failures.push({ file, message: 'nullish coalescing (??) found in <template> (buble rejects it)' })
176
+ }
177
+ }
178
+ report(failures)
179
+ process.exit(failures.length === 0 ? 0 : 1)
180
+ }