@conduction/nextcloud-vue 2.2.0-vue3.13 → 2.2.0-vue3.15
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 +3 -1
- package/scripts/.jsdoc-baselines.json +254 -0
- package/scripts/build-validators.js +174 -0
- package/scripts/check-css-entry.js +118 -0
- package/scripts/check-docs.js +724 -0
- package/scripts/check-integration-build.js +180 -0
- package/scripts/check-integration-parity.js +540 -0
- package/scripts/check-jsdoc.js +230 -0
- package/scripts/check-peer-consistency.js +161 -0
- package/scripts/generate-nl-icons.mjs +98 -0
- package/scripts/precommit-regenerate-features.sh +33 -0
- package/scripts/precommit-regenerate-partials.sh +68 -0
- package/scripts/sync-l10n-ellipsis.js +46 -0
- package/scripts/update-fleet-manifest-fixtures.js +116 -0
- package/scripts/vue3-compile-sweep.cjs +77 -0
- package/stylelint/index.js +61 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
+
//
|
|
4
|
+
// update-fleet-manifest-fixtures.js — refresh the vendored fleet-manifest
|
|
5
|
+
// corpus used by tests/schemas/fleet-manifest-regression.spec.js.
|
|
6
|
+
//
|
|
7
|
+
// Why (2026-07-06 manifest fleet audit, item 15): a schema change in this
|
|
8
|
+
// library can silently invalidate a manifest already deployed in a fleet app
|
|
9
|
+
// — the exact drift the audit chased (installed schema generations spanned
|
|
10
|
+
// beta.30..beta.155). The regression spec validates every deployed manifest
|
|
11
|
+
// against the CURRENT candidate schema on every PR, so a breaking schema
|
|
12
|
+
// change fails HERE, before release, instead of surfacing as install drift.
|
|
13
|
+
//
|
|
14
|
+
// The corpus is a vendored snapshot (CI has no fleet checkout). This script
|
|
15
|
+
// refreshes it from the sibling app checkouts. Run it after a deliberate,
|
|
16
|
+
// reviewed manifest change lands in a fleet app — never to make a failing
|
|
17
|
+
// regression test pass (that would defeat the guard).
|
|
18
|
+
//
|
|
19
|
+
// Usage:
|
|
20
|
+
// node scripts/update-fleet-manifest-fixtures.js [fleet-root]
|
|
21
|
+
// fleet-root — dir whose children are the app checkouts
|
|
22
|
+
// (default: the parent of this library's checkout).
|
|
23
|
+
//
|
|
24
|
+
// Output: writes tests/fixtures/fleet-manifests/<app>.json for every app
|
|
25
|
+
// that ships src/manifest.json, plus an index.json manifest (app -> bytes,
|
|
26
|
+
// $schema, sha256) so the spec can report drift precisely.
|
|
27
|
+
|
|
28
|
+
'use strict'
|
|
29
|
+
|
|
30
|
+
const fs = require('fs')
|
|
31
|
+
const path = require('path')
|
|
32
|
+
const crypto = require('crypto')
|
|
33
|
+
const { execFileSync } = require('child_process')
|
|
34
|
+
|
|
35
|
+
const REPO_ROOT = path.resolve(__dirname, '..')
|
|
36
|
+
const FLEET_ROOT = process.argv[2]
|
|
37
|
+
? path.resolve(process.argv[2])
|
|
38
|
+
: path.resolve(REPO_ROOT, '..')
|
|
39
|
+
const OUT_DIR = path.join(REPO_ROOT, 'tests', 'fixtures', 'fleet-manifests')
|
|
40
|
+
|
|
41
|
+
// Non-app sibling dirs that may carry a src/manifest.json but are not fleet
|
|
42
|
+
// apps we want to gate on (this library's own examples, etc.).
|
|
43
|
+
const SKIP = new Set(['nextcloud-vue'])
|
|
44
|
+
|
|
45
|
+
function sha256(buf) {
|
|
46
|
+
return crypto.createHash('sha256').update(buf).digest('hex')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function main() {
|
|
50
|
+
if (fs.existsSync(OUT_DIR) === false) {
|
|
51
|
+
fs.mkdirSync(OUT_DIR, { recursive: true })
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const index = {}
|
|
55
|
+
let written = 0
|
|
56
|
+
|
|
57
|
+
for (const app of fs.readdirSync(FLEET_ROOT).sort()) {
|
|
58
|
+
if (SKIP.has(app)) {
|
|
59
|
+
continue
|
|
60
|
+
}
|
|
61
|
+
const appDir = path.join(FLEET_ROOT, app)
|
|
62
|
+
const manifestPath = path.join(appDir, 'src', 'manifest.json')
|
|
63
|
+
if (fs.existsSync(manifestPath) === false) {
|
|
64
|
+
continue
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// The corpus must reflect the DEPLOYED manifest — what is merged to the
|
|
68
|
+
// app's integration branch — not whatever a local checkout happens to
|
|
69
|
+
// have on a feature branch. Prefer the committed origin/development (or
|
|
70
|
+
// origin/main) blob; fall back to the working tree only if git can't
|
|
71
|
+
// resolve one (e.g. a shallow CI checkout).
|
|
72
|
+
let raw = null
|
|
73
|
+
for (const ref of ['origin/development', 'origin/main']) {
|
|
74
|
+
try {
|
|
75
|
+
raw = execFileSync('git', ['-C', appDir, 'show', `${ref}:src/manifest.json`], { maxBuffer: 32 * 1024 * 1024 })
|
|
76
|
+
break
|
|
77
|
+
} catch (_) { /* try next ref */ }
|
|
78
|
+
}
|
|
79
|
+
if (raw === null) {
|
|
80
|
+
try {
|
|
81
|
+
raw = fs.readFileSync(manifestPath)
|
|
82
|
+
console.error(` (note: ${app} — no origin/development|main blob, snapshotting working tree)`)
|
|
83
|
+
} catch (_) {
|
|
84
|
+
continue
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Validate it is parseable JSON before vendoring — never snapshot
|
|
89
|
+
// a corrupt file into the corpus.
|
|
90
|
+
let parsed
|
|
91
|
+
try {
|
|
92
|
+
parsed = JSON.parse(raw)
|
|
93
|
+
} catch (e) {
|
|
94
|
+
console.error(`SKIP ${app}: manifest.json is not valid JSON (${e.message})`)
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const pretty = JSON.stringify(parsed, null, '\t') + '\n'
|
|
99
|
+
fs.writeFileSync(path.join(OUT_DIR, `${app}.json`), pretty)
|
|
100
|
+
index[app] = {
|
|
101
|
+
bytes: Buffer.byteLength(pretty),
|
|
102
|
+
$schema: typeof parsed.$schema === 'string' ? parsed.$schema : null,
|
|
103
|
+
sha256: sha256(pretty),
|
|
104
|
+
}
|
|
105
|
+
written += 1
|
|
106
|
+
console.log(` ${app} (${index[app].bytes} bytes)`)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
fs.writeFileSync(
|
|
110
|
+
path.join(OUT_DIR, 'index.json'),
|
|
111
|
+
JSON.stringify(index, null, '\t') + '\n',
|
|
112
|
+
)
|
|
113
|
+
console.log(`\nWrote ${written} fleet manifests + index.json to ${path.relative(REPO_ROOT, OUT_DIR)}`)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
main()
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vue 3 compile-readiness sweep (ADR-066, openspec vue-3-migration).
|
|
3
|
+
*
|
|
4
|
+
* Compiles every SFC template + script under src/ with @vue/compiler-sfc in
|
|
5
|
+
* @vue/compat MODE 2, and reports which components fail to COMPILE on Vue 3.
|
|
6
|
+
* This is the fastest signal for the migration: compile failures are hard
|
|
7
|
+
* blockers, and this needs no bundle/install of the runtime deps.
|
|
8
|
+
*
|
|
9
|
+
* Requires the Vue 3 toolchain (`npm i -D @vue/compiler-sfc@^3.5`). Run:
|
|
10
|
+
* node scripts/vue3-compile-sweep.cjs (or: npm run check:vue3-compile)
|
|
11
|
+
*
|
|
12
|
+
* Exit code is the number of failing components (0 = all clean).
|
|
13
|
+
*
|
|
14
|
+
* NOTE: a clean sweep does NOT mean runtime-correct. Plain Vue 3 silently
|
|
15
|
+
* mis-compiles `.sync` and `{{x|f}}` (see BUILD-VUE3.md); compat MODE 2 keeps
|
|
16
|
+
* them correct here, but every such site must still be rewritten (tasks 2.2/2.6)
|
|
17
|
+
* before the compat flags come off.
|
|
18
|
+
*/
|
|
19
|
+
const fs = require('fs')
|
|
20
|
+
const path = require('path')
|
|
21
|
+
|
|
22
|
+
let sfc
|
|
23
|
+
try {
|
|
24
|
+
sfc = require('@vue/compiler-sfc')
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.error('[vue3-compile-sweep] @vue/compiler-sfc not found — install the Vue 3 toolchain first (npm i -D @vue/compiler-sfc@^3.5).')
|
|
27
|
+
process.exit(2)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const root = path.resolve(__dirname, '..', 'src')
|
|
31
|
+
const compat = { compatConfig: { MODE: 2, COMPILER_FILTERS: true } }
|
|
32
|
+
|
|
33
|
+
function walk(dir, out = []) {
|
|
34
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
35
|
+
const p = path.join(dir, e.name)
|
|
36
|
+
if (e.isDirectory()) walk(p, out)
|
|
37
|
+
else if (e.name.endsWith('.vue')) out.push(p)
|
|
38
|
+
}
|
|
39
|
+
return out
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const files = walk(root)
|
|
43
|
+
let clean = 0
|
|
44
|
+
const failed = []
|
|
45
|
+
|
|
46
|
+
for (const f of files) {
|
|
47
|
+
const src = fs.readFileSync(f, 'utf8')
|
|
48
|
+
const errs = []
|
|
49
|
+
try {
|
|
50
|
+
const { descriptor, errors } = sfc.parse(src, { filename: f })
|
|
51
|
+
errs.push(...(errors || []).map((e) => 'parse: ' + (e.message || e)))
|
|
52
|
+
if (descriptor.template) {
|
|
53
|
+
const t = sfc.compileTemplate({ source: descriptor.template.content, filename: f, id: 'x', compilerOptions: compat })
|
|
54
|
+
errs.push(...(t.errors || []).map((e) => 'tmpl: ' + (e.message || String(e)).slice(0, 100)))
|
|
55
|
+
}
|
|
56
|
+
if (descriptor.script || descriptor.scriptSetup) {
|
|
57
|
+
try {
|
|
58
|
+
sfc.compileScript(descriptor, { id: 'x', templateOptions: { compilerOptions: compat } })
|
|
59
|
+
} catch (e) {
|
|
60
|
+
errs.push('script: ' + (e.message || String(e)).slice(0, 100))
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} catch (e) {
|
|
64
|
+
errs.push('fatal: ' + (e.message || String(e)).slice(0, 100))
|
|
65
|
+
}
|
|
66
|
+
if (errs.length === 0) clean++
|
|
67
|
+
else failed.push({ f: path.relative(root, f), errs })
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
console.log(`Vue 3 compile sweep (compat MODE 2) — ${files.length} components`)
|
|
71
|
+
console.log(` clean : ${clean}`)
|
|
72
|
+
console.log(` failed: ${failed.length}`)
|
|
73
|
+
if (failed.length) {
|
|
74
|
+
console.log('')
|
|
75
|
+
for (const x of failed) console.log(` ✗ ${x.f}\n ${x.errs[0]}`)
|
|
76
|
+
}
|
|
77
|
+
process.exit(failed.length)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
3
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
4
|
+
*
|
|
5
|
+
* `@conduction/nextcloud-vue/stylelint` — the shared Stylelint preset for every
|
|
6
|
+
* Conduction Nextcloud app.
|
|
7
|
+
*
|
|
8
|
+
* WHY THIS LIVES HERE
|
|
9
|
+
* -------------------
|
|
10
|
+
* The same reason the ESLint preset does, and the same reason the PHP ruleset
|
|
11
|
+
* moved to `conduction/hydra-gates`: a copied config is not a shared config.
|
|
12
|
+
*
|
|
13
|
+
* Measured across the 18 core apps on 2026-08-12, `stylelint.config.js` existed
|
|
14
|
+
* in six variants. Ten apps were byte-identical to this file; the other eight
|
|
15
|
+
* had each drifted separately. None of that drift was a decision.
|
|
16
|
+
*
|
|
17
|
+
* Stylelint cannot be homed the way PHPCS was, through a path into `vendor/`,
|
|
18
|
+
* because a Stylelint config resolves `extends` against `node_modules` relative
|
|
19
|
+
* to itself. An npm package is the only channel that works — and every app
|
|
20
|
+
* already depends on this one.
|
|
21
|
+
*
|
|
22
|
+
* WHAT IT IS
|
|
23
|
+
* ----------
|
|
24
|
+
* `@nextcloud/stylelint-config`, plus exactly one addition.
|
|
25
|
+
*
|
|
26
|
+
* That is deliberate and it mirrors `conduction/coding-standard` on the PHP
|
|
27
|
+
* side: Conduction code must pass Nextcloud's own checks unchanged. We may be
|
|
28
|
+
* STRICTER than Nextcloud; we may not be DIFFERENT from it. Anything here that
|
|
29
|
+
* contradicted `@nextcloud/stylelint-config` would put an app in the position
|
|
30
|
+
* the PHP toolchain was in until this week — two tools with overlapping
|
|
31
|
+
* jurisdiction demanding opposite things, and no way to satisfy both.
|
|
32
|
+
*
|
|
33
|
+
* THE ONE ADDITION
|
|
34
|
+
* ----------------
|
|
35
|
+
* `::v-deep` is a Vue SFC scoped-style selector, not a CSS pseudo-element.
|
|
36
|
+
* Stylelint's `selector-pseudo-element-no-unknown` does not know it and flags
|
|
37
|
+
* every use. Nextcloud's config does not carry the exception because Nextcloud
|
|
38
|
+
* core does not use `::v-deep`; this fleet does, in every app that restyles a
|
|
39
|
+
* child component's internals.
|
|
40
|
+
*
|
|
41
|
+
* This is additive in the strict sense — it relaxes a rule on a token Nextcloud
|
|
42
|
+
* never emits — so a file that satisfies this preset still satisfies theirs.
|
|
43
|
+
*
|
|
44
|
+
* USAGE
|
|
45
|
+
* -----
|
|
46
|
+
* // stylelint.config.js
|
|
47
|
+
* module.exports = require('@conduction/nextcloud-vue/stylelint')
|
|
48
|
+
*
|
|
49
|
+
* To add an app-specific rule, spread it — do not redefine `extends`:
|
|
50
|
+
*
|
|
51
|
+
* const base = require('@conduction/nextcloud-vue/stylelint')
|
|
52
|
+
* module.exports = { ...base, rules: { ...base.rules, 'my/rule': true } }
|
|
53
|
+
*/
|
|
54
|
+
module.exports = {
|
|
55
|
+
extends: '@nextcloud/stylelint-config',
|
|
56
|
+
rules: {
|
|
57
|
+
'selector-pseudo-element-no-unknown': [true, {
|
|
58
|
+
ignorePseudoElements: ['v-deep'],
|
|
59
|
+
}],
|
|
60
|
+
},
|
|
61
|
+
}
|