diff --git a/.github/eslint-plugin/js-rule-stub.md b/.github/eslint-plugin/js-rule-stub.md index 8fce4bb4e1..c592bc247e 100644 --- a/.github/eslint-plugin/js-rule-stub.md +++ b/.github/eslint-plugin/js-rule-stub.md @@ -1,7 +1,3 @@ ---- -status: released ---- - diff --git a/.vitepress/config.js b/.vitepress/config.js index 7b39a57aef..0bfab873f3 100644 --- a/.vitepress/config.js +++ b/.vitepress/config.js @@ -2,13 +2,11 @@ const base = process.env.GH_BASE || '/docs/' // Construct vitepress config object... +import path from 'node:path' import { defineConfig } from 'vitepress' import languages from './languages' -import path from 'node:path' import { Menu } from './menu.js' -const menu = await Menu.from ('./menu.md') - const config = defineConfig({ title: 'capire', @@ -26,8 +24,12 @@ const config = defineConfig({ '**/LICENSE.md', '**/CONTRIBUTING.md', '**/CODE_OF_CONDUCT.md', + '**/redirects.md', '**/menu.md', - '**/-*.md' + '**/_menu.md', + '**/-*.md', + '**/internal.md', + '**/FIXME.md', ], markdown: { @@ -35,12 +37,20 @@ const config = defineConfig({ toc: { level: [2,3] }, - }, + container: { // Doesn't seem to work yet + infoLabel: 'Info', + noteLabel: 'Note', + tipLabel: 'Tip', + warningLabel: 'Warning', + dangerLabel: 'Danger!', + cautionLabel: 'Caution!', + importantLabel: 'Important!', + detailsLabel: 'Details' + } + }, themeConfig: { - sidebar: menu.items, - nav: menu.navbar, - logo: '/cap-logo.svg', + logo: '/logos/cap.svg', outline: [2,3], socialLinks: [ { icon: 'github', link: 'https://github.com/capire/docs' } @@ -64,7 +74,7 @@ const config = defineConfig({ ['meta', { 'http-equiv': 'Content-Security-Policy', content: "script-src 'self' https://www.capire-matomo.cloud.sap 'unsafe-inline' 'unsafe-eval'" }], ['link', { rel: 'icon', href: base+'favicon.ico' }], ['link', { rel: 'shortcut icon', href: base+'favicon.ico' }], - ['link', { rel: 'apple-touch-icon', sizes: '180x180', href: base+'cap-logo.png' }], + ['link', { rel: 'apple-touch-icon', sizes: '180x180', href: base+'logos/cap.png' }], ['script', { src: base+'script.js' } ] ], @@ -95,21 +105,22 @@ import rewrites from './rewrites' config.rewrites = rewrites // Read menu from local menu.md, but only if we run standalone, not embeded as @external -// if (process.cwd() === path.dirname(__dirname)) { -// const menu_md = path.resolve (__filename,'../../menu.md') -// const Menu = await import('./menu') -// const menu = await Menu.from (menu_md, rewrites) -// config.themeConfig.sidebar = menu.items -// config.themeConfig.nav = menu.navbar -// } +if (process.cwd() === path.dirname(__dirname)) { + const menu = await Menu.from ('./menu.md', rewrites) + config.themeConfig.sidebar = menu.items + config.themeConfig.nav = menu.navbar +} // Add custom capire info to the theme config +const siteURL = new URL(process.env.SITE_HOSTNAME || 'http://localhost:4173/docs/') +if (!siteURL.pathname.endsWith('/')) siteURL.pathname += '/' config.themeConfig.capire = { versions: { - java_services: '4.6.0', - java_cds4j: '4.6.0' + java_services: '4.7.0', + java_cds4j: '4.7.0' }, - gotoLinks: [] + gotoLinks: [], + siteURL } // Add meta tag to prevent indexing of preview deployments @@ -173,32 +184,28 @@ config.themeConfig.search = { } } -// Add twoslash transformer to the markdown config +// Add twoslash transformer to the markdown config (if requested as it slows down builds) import { transformerTwoslash } from '@shikijs/vitepress-twoslash' -config.markdown.codeTransformers = [ - transformerTwoslash() -] +if (process.env.VITE_CAPIRE_EXTRA_ASSETS) { + config.markdown.codeTransformers = [ transformerTwoslash() ] +} // Add custom markdown renderers... +import { dl } from '@mdit/plugin-dl' import * as MdAttrsPropagate from './lib/md-attrs-propagate' import * as MdTypedModels from './lib/md-typed-models' + config.markdown.config = md => { MdAttrsPropagate.install(md) MdTypedModels.install(md) -} - -// Add sitemap -const siteURL = new URL(process.env.SITE_HOSTNAME || 'http://localhost:4173/docs') -if (!siteURL.pathname.endsWith('/')) siteURL.pathname += '/' -config.sitemap = { - hostname: siteURL.href + md.use(dl) } // Add custom buildEnd hook -import * as cdsMavenSite from './lib/cds-maven-site' import { promises as fs } from 'node:fs' +import * as cdsMavenSite from './lib/cds-maven-site' config.buildEnd = async ({ outDir, site }) => { - const sitemapURL = new URL(siteURL.href) + const sitemapURL = new URL(config.themeConfig.capire.siteURL.href) sitemapURL.pathname = path.join(sitemapURL.pathname, 'sitemap.xml') console.debug('✓ writing robots.txt with sitemap URL', sitemapURL.href) // eslint-disable-line no-console const robots = (await fs.readFile(path.resolve(__dirname, 'robots.txt'))).toString().replace('{{SITEMAP}}', sitemapURL.href) @@ -206,13 +213,6 @@ config.buildEnd = async ({ outDir, site }) => { // disabled by default to avoid online fetches during local build if (process.env.VITE_CAPIRE_EXTRA_ASSETS) { - // zip assets aren't copied automatically, and `vite.assetInclude` doesn't work either - const hanaAssetDir = 'advanced/assets' - const hanaAsset = path.join(hanaAssetDir, 'native-hana-samples.zip') - await fs.mkdir(path.join(outDir, hanaAssetDir), {recursive: true}) - console.debug('✓ copying HANA assets to ', path.join(outDir, hanaAsset)) // eslint-disable-line no-console - - await fs.copyFile(path.join(__dirname, '..', hanaAsset), path.join(outDir, hanaAsset)) await cdsMavenSite.copySiteAssets(path.join(outDir, 'java/assets/cds-maven-plugin-site'), site) } } diff --git a/.vitepress/languages/cds.tmLanguage.json b/.vitepress/languages/cds.tmLanguage.json index f1a851035f..21b90211e0 100644 --- a/.vitepress/languages/cds.tmLanguage.json +++ b/.vitepress/languages/cds.tmLanguage.json @@ -3,112 +3,25 @@ "fileTypes": [ "cds" ], - "name": "CDS Definition Language (CDL)", + "name": "CDS", "patterns": [ { - "comment": "entityDef, aspectDef, and others containing elements", - "begin": "\\b(aspect|(abstract\\s+)?entity|type|event)\\b", - "beginCaptures": { - "1": { - "name": "keyword.strong.cds" - } - }, - "end": "(?<=})(;)?|(;)", - "endCaptures": { - "1": { - "name": "punctuation.terminator.statement.cds" - }, - "2": { - "name": "punctuation.terminator.statement.cds" - } - }, - "patterns": [ - { - "include": "#atAnnoParen" - }, - { - "include": "#atAnnoNoParen" - }, - { - "comment": "Aspects", - "begin": ":", - "end": "(?=[{;@])", - "beginCaptures": { - "0": { - "name": "keyword.operator.cds" - } - }, - "patterns": [ - { - "include": "#identifiers" - }, - { - "match": ",", - "name": "punctuation.separator.object.cds" - } - ] - }, - { - "include": "#bracedElementDef" - }, - { - "include": "#keywords" - }, - { - "include": "#identifiers" - } - ] + "include": "#serviceDef" }, { - "comment": "extendVarious (TODO simplify begin pattern - would currently produce false positives; allow annotationAssignment_ll1)", - "begin": "(?i)\\b(extend)\\s+((context|service|aspect|entity|projection|type)\\s+)?((?!@)\\S+)(\\s+(with)(\\s+(actions|definitions|columns|elements|enum))?|(?=\\s*{))", - "beginCaptures": { - "1": { - "name": "keyword.strong.cds" - }, - "3": { - "name": "keyword.cds" - }, - "4": { - "name": "entity.name.type.cds" - }, - "6": { - "name": "keyword.cds" - }, - "8": { - "name": "keyword.cds" - } - }, - "end": "(?<=})(;)?|(;)", - "endCaptures": { - "1": { - "name": "punctuation.terminator.statement.cds" - }, - "2": { - "name": "punctuation.terminator.statement.cds" - } - }, - "patterns": [ - { - "include": "#bracedElementDef" - }, - { - "include": "#atAnnoParen" - }, - { - "include": "#atAnnoNoParen" - }, - { - "include": "#keywords" - }, - { - "include": "#identifiers" - }, - { - "match": ",", - "name": "punctuation.separator.object.cds" - } - ] + "include": "#actionOrFunctionDef" + }, + { + "include": "#aspectDefOrEntityDefOrTypeDefOrEventDefOrAnnotate" + }, + { + "include": "#extendArtifactWithDef" + }, + { + "include": "#extendAspectOrEntityOrTypeOrArtifact" + }, + { + "include": "#extendProjection" }, { "match": "(?=|<>|<|>", @@ -590,7 +464,8 @@ } ] }, - "strings": { + "string": { + "name": "string", "patterns": [ { "begin": "'", @@ -636,28 +511,34 @@ "include": "#interpolation" }, { - "include": "#escapes" + "include": "#escape" } ] } ] }, - "escapes": { + "escape": { "match": "\\\\([xu$]\\{?[0-9a-fA-F]+}?|.|$)", "name": "constant.character.escape.cds" }, "selectItemDef": { - "begin": "^\\s*(?=\\*|.+\\s+as\\s+)", + "name": "selectItemDef", + "begin": "(?!\\s*@)", "end": "(?=})|(,)", + "endCaptures": { + "1": { + "name": "punctuation.separator.object.cds" + } + }, "patterns": [ { - "include": "#bracedElementDef" + "include": "#bracedSelectItemDef" }, { - "include": "#strings" + "include": "#string" }, { - "include": "#comments" + "include": "#comment" }, { "include": "#atAnnoParen" @@ -666,30 +547,30 @@ "include": "#atAnnoNoParen" }, { - "include": "#keywords" + "include": "#keyword" }, { "include": "#bracketedExpression" }, { "comment": "Element name or type", - "match": "(? console.debug ('[menu.js] -', ...args) : undefined +const DEBUG = process.env.DEBUG?.match(/\b(menu|all)\b/) ? (...args) => console.debug ('[menu.js] -', ...args) : undefined +const EXTERNAL = process.env.VITE_CAPIRE_ENV === 'external' const cwd = process.cwd() @@ -47,9 +51,11 @@ export class MenuItem { async include (filename, parent='.', _rewrite = rewrites, include, exclude) { const root = dirname(parent), folder = dirname(filename) const rewrite = link => link[0] === '/' ? link : _rewrite (normalize(join(folder,link))) - const {items} = await Menu.from (join(root,filename), rewrite, include, exclude) - const children = this.items ??= []; children.push (...items) - this.link = '/'+folder+'/' + const {items, skipped} = await Menu.from (join(root,filename), rewrite, include, exclude) + if (items) (this.items ??= []).push (...items) + if (skipped) (this.skipped ??= []).push (...skipped) + const index = existsSync (join (root,folder,'index.md')) + if (index) this.link = `/${folder}/`; else delete this.link this.collapsed = true } } @@ -76,16 +82,37 @@ export class Menu extends MenuItem { /^\s*(#+)\s(.*)/.exec(each) || [] // without link if (!hashes) continue //> skip lines not starting with #es + let is_submenu = /\/(_?menu.md)$/.exec(link) + + if (EXTERNAL) { + let unreleased = each.match(//) + if (unreleased) { + if (link) { + let target + if (is_submenu) { // skip entire submenu by with ** entry + target = resolve (cwd, dirname(file), dirname(link), '**') + } else { + target = resolve (cwd, dirname(file), link.replace(/^\//, '').replace(/\/$/,'/index.md')) + if (!target.endsWith('.md')) target += '.md' + } + (menu.skipped ??= []).push (relative(cwd,target)) + DEBUG?.('skipped:', relative(cwd,target), 'at', relative(cwd,file)+':'+(i+1)) + } else { + DEBUG?.('skipped:', unreleased[0], text, 'at', relative(cwd,file)+':'+(i+1)) + } + continue + } + } + // Get parent from stack -> it's the recent stack entry with less hashes let parent = children [hashes.length-1] if (!parent) throw new Error (`Missing parent for: ${each.trim()} at ${relative(cwd,file)}:${i+1}`) // Rewrite link and skip if excluded - let is_submenu = /\/(_?menu.md)$/.exec(link) if (link) { if (link[0] !== '/' && !is_submenu) link = rewrite(link) if (exclude(link) || !include(link)) { - DEBUG?.('skipped:', each.trim(), 'at', relative(cwd,file)+'.'+(i+1)) + DEBUG?.('excluded:', each.trim(), 'at', relative(cwd,file)+':'+(i+1)) continue } } @@ -113,6 +140,20 @@ export class Menu extends MenuItem { } set navbar (v) { super.navbar = v } + // returns skipped entries from all submenus + get excluded() { + let all = this.skipped || [] + const collect = items => { + for (let item of items) { + if (item.skipped) all.push (...item.skipped) + if (item.items) collect (item.items) + } + } + collect (this.items || []) + return this.excluded = all + } + set excluded (v) { super.excluded = v } + /** * CLI methods for ad-hoc tests @@ -131,6 +172,7 @@ export class Menu extends MenuItem { // Parse menu.md file(s) with optional rewrites const {default:rewrites} = options.rewrites ? await import (resolve (options.rewrites)) : {} const menu = await this.from (args.shift(), rewrites) + menu.excluded = menu.excluded // trigger calculation of excluded entries // Print result const result = options.navbar ? menu.navbar : menu diff --git a/.vitepress/theme/Layout.vue b/.vitepress/theme/Layout.vue index 3c4f1bcf77..d85b1d08bd 100644 --- a/.vitepress/theme/Layout.vue +++ b/.vitepress/theme/Layout.vue @@ -1,36 +1,30 @@ - + + {{ archiveVersion }}
Archive +
+ DEV PREVIEW
See cap.cloud.sap
diff --git a/.vitepress/theme/alerts.scss b/.vitepress/theme/alerts.scss new file mode 100644 index 0000000000..c66197d90c --- /dev/null +++ b/.vitepress/theme/alerts.scss @@ -0,0 +1,142 @@ +// Defaults from https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css: +// --vp-custom-block-tip-border: transparent; +// --vp-custom-block-tip-text: var(--vp-c-text-1); +// --vp-custom-block-tip-bg: var(--vp-c-tip-soft); +// --vp-custom-block-tip-code-bg: var(--vp-c-tip-soft); +// --vp-c-tip-1: var(--vp-c-brand-1); +// --vp-c-tip-2: var(--vp-c-brand-2); +// --vp-c-tip-3: var(--vp-c-brand-3); +// --vp-c-tip-bg: var(--vp-c-brand-soft); +// --vp-c-tip-soft: var(--vp-c-brand-soft); + +:root { + --vp-c-info-2: var(--vp-c-text-3); + --vp-c-info-1: var(--vp-c-text-2); + --vp-c-note-1: var(--vp-c-text-1); + --vp-c-tip-1: var(--vp-c-green-1); + --vp-c-warning-1: var(--vp-c-yellow-1); + + --vp-custom-block-note-border: var(--vp-c-note-1); + --vp-custom-block-info-border: var(--vp-c-info-2); + --vp-custom-block-tip-border: var(--vp-c-tip-1); + --vp-custom-block-important-border: var(--vp-c-important-1); + --vp-custom-block-warning-border: var(--vp-c-warning-1); + --vp-custom-block-caution-border: var(--vp-c-caution-1); + --vp-custom-block-danger-border: var(--vp-c-danger-1); +} + +.vp-doc .custom-block { + + font-size: 100%; // VitePress chooses 90% -> reset to normal font size + &.info, &.note { font-size: 90%; } // make info and note smaller + + &:has(p:only-child) { + border:none; + margin-top: 1em; + margin-bottom: 1em; + } + + // Borders and background styles + background-color: transparent !important; + padding: .1em 0 0 1em; + margin: 2em 0; + border-width: 0 0 0 4px; + border-radius: 0; + + &.details { + border: 4px solid transparent; + border-width: 0 0 0 3px; + padding: .2em 0 .2em 1em; + margin: 0; + &[open] { + border-color: var(--vp-custom-block-info-border); + border-radius: 14px; + } + summary:first-of-type::before { + padding-left: 5px; + content: ''; + } + summary { + font-weight: 500; + font-style: italic; + color: var(--vp-c-text-2); + } + > p, ul, ol { + font-size: 90%; + } + } + + &.info .custom-block-title { + color: var(--vp-c-info-1); + &::before { + background-color: var(--vp-c-info-1); + mask: url('/icons/note.svg'); + } + } + + &.note .custom-block-title { + color: var(--vp-c-note-1); + &::before { + background-color: var(--vp-c-note-1); + mask: url('/icons/note.svg'); + } + } + + &.tip .custom-block-title { + color: var(--vp-c-tip-1); + &::before { + background-color: var(--vp-c-tip-1); + mask: url('/icons/tip.svg'); + } + } + + &.important .custom-block-title { + color: var(--vp-c-important-1); + &::before { + background-color: var(--vp-c-important-1); + mask: url('/icons/important.svg'); + } + } + + &.warning .custom-block-title { + color: var(--vp-c-warning-1); + &::before { + background-color: var(--vp-c-warning-1); + mask: url('/icons/warning.svg'); + } + } + + &.caution .custom-block-title { + color: var(--vp-c-caution-1); + &::before { + background-color: var(--vp-c-caution-1); + mask: url('/icons/caution.svg'); + } + } + + &.danger .custom-block-title { + color: var(--vp-c-danger-1); + &::before { + background-color: var(--vp-c-danger-1); + mask: url('/icons/caution.svg'); + } + } + + // Title icon styles + .custom-block-title::before { + background-color: var(--vp-c-text-1); //> for masks + vertical-align: middle; + display: inline-block; + content: ''; + height: 20px; + width: 20px; + margin-top: -2px; + margin-right: .7em; + } + + // a { + // font-weight: inherit !important; + // text-decoration-style: dotted; + // text-decoration: underline; + // } +} diff --git a/.vitepress/theme/code.scss b/.vitepress/theme/code.scss new file mode 100644 index 0000000000..20fce2e416 --- /dev/null +++ b/.vitepress/theme/code.scss @@ -0,0 +1,86 @@ + +main .vp-doc { + + div.vp-code-group + div[class*='language-'] { + margin-top: -8px !important; // override code group margin + } + + div.language-zsh pre code { + line-height: 1.4em + } + + pre { + padding: 12px 0 !important; + code { + padding: 0 22px !important; + } + } + div[class*='language-'] > button.copy { + top: 4px; // Fix for copy button position + } + + :not(pre) code.no-bg { background-color: initial; } + :not(pre) > code { + font-style: italic; + font-size: 90%; + color: unset; // revert the c-brand-1 from default theme + } + + // code blocks without background, e.g. as sole values in table cells + + a { + text-decoration: none; + &:hover { + text-decoration: underline; + } + } + + pre.log { + font-size: var(--vp-code-font-size); + line-height: var(--vp-code-line-height); + background: var(--vp-code-block-bg); + padding: 12px 22px !important; + overflow-x: auto; + margin: 16px -24px; // same negative margin technique is used in Vitepress + @media (min-width: 640px) { + margin: unset; + border-radius: 8px; + } + span { + &.cmd { + color: #6F42C1; + .dark & { color: #B392F0; } + } + &.args { + color: #032F62; + .dark & { color: #9ECBFF; } + } + &.flags, &.option, &.posArg { + color: #005CC5; + .dark & { color: #79B8FF; } + } + &.cwd { + color: #777; + .dark & { color: #999; } + } + &.livereload { + color: #FDEC87 + } + } + em { + color: #00ab00e0; + font-style: normal; + } + b { + color: #ee0000e0; + } + i { + color: grey; + } + } + + .line-numbers-wrapper { + padding-top: 13px; // align with code line height + } + +} diff --git a/.vitepress/theme/components/ScrollToTop.vue b/.vitepress/theme/components/ScrollToTop.vue index a6bd1e77be..0267e76309 100644 --- a/.vitepress/theme/components/ScrollToTop.vue +++ b/.vitepress/theme/components/ScrollToTop.vue @@ -6,47 +6,54 @@ Scroll to top + d="M256 -32q53 0 99 20t81 55t55 81.5t20 99t-20 99t-55 81.5t-81 55t-99 20t-99.5 -20t-81.5 -55t-55 -81.5t-20 -99t20 -99t55 -81.5t81.5 -55t99.5 -20zM256 428q42 0 79 -16t65 -44t44 -65t16 -79t-16 -79.5t-44 -65.5t-65 -44t-79 -16t-79.5 16t-65.5 44t-44 65.5t-16 79.5t16 79t44 65t65.5 44t79.5 16zM314 180q8 -8 18 -8t18 8t8 18t-8 18l-77 77q-8 8 -17.5 8t-17.5 -8l-77 -77q-8 -8 -8 -18t8 -18t18 -8t18 8l58 59z" + /> + + + +/* +.outline-title:hover { color: var(--vp-c-brand-1); } +.outline-title { transition: color 0.3s; cursor: pointer; } +*/ + \ No newline at end of file diff --git a/.vitepress/theme/components/ScrollToTopSimple.vue b/.vitepress/theme/components/ScrollToTopSimple.vue deleted file mode 100644 index 7999c3862a..0000000000 --- a/.vitepress/theme/components/ScrollToTopSimple.vue +++ /dev/null @@ -1,12 +0,0 @@ - -