diff --git a/.github/workflows/semantic_release.yml b/.github/workflows/semantic_release.yml index 50a319d..2536ac4 100644 --- a/.github/workflows/semantic_release.yml +++ b/.github/workflows/semantic_release.yml @@ -42,4 +42,8 @@ jobs: run: | sudo chown $USER:$USER .github/release.sh sudo chmod +x .github/release.sh - npx -p conventional-changelog-conventionalcommits@4 -p @semantic-release/exec -p @semantic-release/git -p semantic-release@18 semantic-release + npx -p conventional-changelog-conventionalcommits@4 \ + -p @semantic-release/exec \ + -p @semantic-release/git \ + -p semantic-release@18 \ + semantic-release diff --git a/.scripts/doc-deps.yml b/.scripts/doc-deps.yml new file mode 100644 index 0000000..ae4a6d4 --- /dev/null +++ b/.scripts/doc-deps.yml @@ -0,0 +1,5 @@ +components: +- name: components-api + url: https://github.com/lenra-io/components-api + version: v1.0.0-beta.63 + file: lenra-api-docs-v1.0.0-beta.63.zip diff --git a/.scripts/load-api.js b/.scripts/load-api.js index abd94b9..d35e8f0 100644 --- a/.scripts/load-api.js +++ b/.scripts/load-api.js @@ -1,16 +1,64 @@ const { exec } = require('child_process'); const Path = require('path'); const fs = require('fs-extra'); +const yaml = require('yaml'); +const https = require('https'); // or 'http' for http:// URLs +const jszip = require('jszip'); +const { default: js } = require('minify/lib/js'); const srcPath = Path.join(__dirname, '..', 'src'); const apiPath = Path.join(srcPath, 'api'); -let cloneApi = true; +const components_yaml = yaml.parse(fs.readFileSync(Path.join(__dirname, 'doc-deps.yml')).toString()) -if (fs.existsSync(apiPath)) { - exec('git pull', {cwd: apiPath}); -} +for (const component of components_yaml.components) { + const component_path = Path.join(apiPath, component.name) + const component_version_file = Path.join(component_path, '.version') + + if (!fs.existsSync(component_version_file) || fs.readFileSync(component_version_file).toString() != component.version) { + if(fs.existsSync(component_path)) + fs.rmSync(component_path, { recursive: true}) + + fs.mkdirSync(component_path, { recursive: true }) + + const file_path = Path.join(component_path, component.file) + const downloaded_stream = fs.createWriteStream(file_path); + + const url = `${component.url}/releases/download/${component.version}/${component.file}` + const download_zip = function (response) { + response.pipe(downloaded_stream) -if (cloneApi) { - exec('git clone https://github.com/lenra-io/components-api.git api', {cwd: srcPath}); -} \ No newline at end of file + // after download completed close filestream + downloaded_stream.on("finish", async () => { + downloaded_stream.close() + + console.log(`Download of ${component.name} (${downloaded_stream.bytesWritten} bytes) Completed`) + + console.log(`Unzip ${Path.basename(file_path)} ...`) + const file_content = fs.readFileSync(file_path) + const jszip_instance = new jszip() + const result = await jszip_instance.loadAsync(file_content) + for(key of Object.keys(result.files)) { + const item = result.files[key]; + if (item.dir) { + fs.mkdirSync(Path.join(component_path, item.name)) + } else { + fs.writeFileSync(Path.join(component_path, item.name), Buffer.from(await item.async('arrayBuffer'))) + } + } + fs.rmSync(file_path) + fs.writeFileSync(component_version_file, component.version) + }) + } + https.get(url, (response) => { + console.log(`Downloading ${url} ...`) + + if (response.statusCode == 200) { + download_zip(response) + } else if (response.statusCode == 302) { + console.log(`Redirecting to ${response.headers.location}`) + https.get(response.headers.location, download_zip) + } + }) + } +} diff --git a/package-lock.json b/package-lock.json index 87b1795..7798ea6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "express": "^4.17.1", "fs-extra": "^10.0.0", + "glob": "^8.0.3", "minify": "^7.0.2", "pug": "^3.0.2", "showdown": "^1.9.1" @@ -18,7 +19,9 @@ "@types/fs-extra": "^9.0.12", "@types/minify": "^6.0.1", "@types/pug": "^2.0.5", - "nodemon": "^2.0.12" + "jszip": "^3.10.1", + "nodemon": "^2.0.12", + "yaml": "^2.1.1" } }, "node_modules/@babel/helper-validator-identifier": { @@ -225,8 +228,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/binary-extensions": { "version": "2.2.0", @@ -684,6 +686,12 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, "node_modules/crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -960,6 +968,11 @@ "node": ">=12" } }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -998,6 +1011,24 @@ "node": ">=6" } }, + "node_modules/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -1010,6 +1041,25 @@ "node": ">= 6" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/global-dirs": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", @@ -1172,6 +1222,12 @@ "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", "dev": true }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, "node_modules/import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", @@ -1190,6 +1246,15 @@ "node": ">=0.8.19" } }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -1373,6 +1438,12 @@ "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", "dev": true }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, "node_modules/jju": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", @@ -1409,6 +1480,18 @@ "promise": "^7.0.1" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, "node_modules/keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", @@ -1430,6 +1513,15 @@ "node": ">=8" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/locate-path": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.1.0.tgz", @@ -1753,7 +1845,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "dependencies": { "wrappy": "1" } @@ -1827,6 +1918,12 @@ "semver": "bin/semver.js" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -1892,6 +1989,12 @@ "node": ">=4" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, "node_modules/promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", @@ -2117,6 +2220,27 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -2312,6 +2436,12 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -2372,6 +2502,21 @@ "node": ">= 0.8" } }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -2679,6 +2824,12 @@ "node": ">=4" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -2769,8 +2920,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "node_modules/write-file-atomic": { "version": "3.0.3", @@ -2804,6 +2954,15 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/yaml": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz", + "integrity": "sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "14.2.3", "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", @@ -3115,8 +3274,7 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "binary-extensions": { "version": "2.2.0", @@ -3472,6 +3630,12 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, "crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -3687,6 +3851,11 @@ "universalify": "^2.0.0" } }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -3716,6 +3885,36 @@ "pump": "^3.0.0" } }, + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -3842,6 +4041,12 @@ "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", "dev": true }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, "import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", @@ -3854,6 +4059,15 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -3986,6 +4200,12 @@ "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", "dev": true }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, "jju": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", @@ -4020,6 +4240,18 @@ "promise": "^7.0.1" } }, + "jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, "keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", @@ -4038,6 +4270,15 @@ "package-json": "^6.3.0" } }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, "locate-path": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.1.0.tgz", @@ -4273,7 +4514,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -4325,6 +4565,12 @@ } } }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, "param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -4375,6 +4621,12 @@ "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", "dev": true }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", @@ -4578,6 +4830,29 @@ } } }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -4724,6 +4999,12 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, "setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -4772,6 +5053,23 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -4996,6 +5294,12 @@ "prepend-http": "^2.0.0" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -5065,8 +5369,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write-file-atomic": { "version": "3.0.3", @@ -5097,6 +5400,12 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "yaml": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz", + "integrity": "sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==", + "dev": true + }, "yargs": { "version": "14.2.3", "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", diff --git a/package.json b/package.json index d4b1be1..e707cf5 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "dependencies": { "express": "^4.17.1", "fs-extra": "^10.0.0", + "glob": "^8.0.3", "minify": "^7.0.2", "pug": "^3.0.2", "showdown": "^1.9.1" @@ -21,6 +22,8 @@ "@types/fs-extra": "^9.0.12", "@types/minify": "^6.0.1", "@types/pug": "^2.0.5", - "nodemon": "^2.0.12" + "jszip": "^3.10.1", + "nodemon": "^2.0.12", + "yaml": "^2.1.1" } } diff --git a/src/build.js b/src/build.js index 76704e9..980bd8a 100644 --- a/src/build.js +++ b/src/build.js @@ -58,10 +58,10 @@ function getPagesToGenerate(page) { ]; } -const traslationsP = Translations.loadTranslations(); +const translationsP = Translations.loadTranslations(); Views.getPages().then(async pages => { - const translations = await traslationsP; + const translations = await translationsP; const langs = Object.keys(translations).sort((a, b) => { const posA = languagePriority.indexOf(a); @@ -120,4 +120,4 @@ Views.getPages().then(async pages => { .join('\n') ); // TODO: manage page modifications to create a sitemap.xml -}); \ No newline at end of file +}); diff --git a/src/i18n/common.json b/src/i18n/common.json index b5cdca6..3dcb9a3 100644 --- a/src/i18n/common.json +++ b/src/i18n/common.json @@ -9,4 +9,4 @@ "position": 1 } } -} \ No newline at end of file +} diff --git a/src/markdown/components-api/components/actionable.md b/src/markdown/components-api/components/actionable.md deleted file mode 100644 index ab47237..0000000 --- a/src/markdown/components-api/components/actionable.md +++ /dev/null @@ -1,78 +0,0 @@ -# actionable - -This component makes its child actionable based on the defined properties. - -When stacking two or more actionables of the same size and using the same attribute, only the lowest one in the tree will be called. - -## Examples - -### Making a text clickable - -```json -{ - "type": "actionable", - "onPressed": { - "action": "textPress", - "props": { - ... - } - }, - "child": { - "type": "text", - "value": "This is an actionable text." - } -} -``` - -### Handling multiple events. - -```json -{ - "type": "actionable", - "onPressed": { - "action": "pressedOnceAction", - }, - "onDoublePressed": { - "action": "doublePressedAction", - }, - "onLongPressed": { - "action": "longPressedAction", - }, - "onHovered": { - "action": "hoveredAction", - }, - "child": { - "type": "text", - "value": "This is an actionable text." - } -} -``` - -### Stacking actionables with same attribute. - - - -```json -{ - "type": "actionable", - "onPressed": { - // Will not be called because there is an actionable below that defines the onPressed attribute - // Note that if this actionable was sized to be bigger than the actionable below, it would be possible to trigger this onPressed - "action": "willNotBeCalled", - }, - "onDoublePressed": { - // Will be called because there is no actionable below that defines this attribute - "action": "willBeCalled", - }, - "child": { - "type": "actionable", - "onPressed": { - "action": "willBeCalled", - }, - "child": { - "type": "text", - "value": "This is an actionable text." - } - } -} -``` \ No newline at end of file diff --git a/src/markdown/components-api/components/button.md b/src/markdown/components-api/components/button.md deleted file mode 100644 index db41ef4..0000000 --- a/src/markdown/components-api/components/button.md +++ /dev/null @@ -1,61 +0,0 @@ -# Button - -This component provides a push button with several presentation options. - -You can set its text, disable it, adjust its size and style, react to a click and add left or right icons. - -## Examples - -### Simple button - -Here is a Button in it's simplest form. -```json -{ - "type": "button", - "text": "My button", - "onPressed": { - "action": "buttonPressed" - }, -} -``` - -### Disabled button - -You can disable the button with a graphical feedback to let the user know he can't interact with it. -```json -{ - "type": "button", - "text": "My button", - "disabled": true, -} -``` - -### Size and style adjusted button - -Buttons also come with a range of different UI styles. -```json -{ - "type": "button", - "text": "My button", - "size": "large", - "mainStyle": "secondary", -} -``` - -### Button with icons - -```json -{ - "type": "button", - "text": "My button", - "leftIcon": { - "type": "icon", - "value": "smart_button", - }, - "rightIcon": { - // You can also use a widget instead of an icon - "type": "text", - "value": "This can be used as an icon" - }, -} -``` diff --git a/src/markdown/components-api/components/checkbox.md b/src/markdown/components-api/components/checkbox.md deleted file mode 100644 index b588b5d..0000000 --- a/src/markdown/components-api/components/checkbox.md +++ /dev/null @@ -1,47 +0,0 @@ -# checkbox - -This component provides a toggleable checkbox with several presentation options. - -## Examples - -### Simple checkbox - -Here is a checkbox in it's simplest form. -```json -{ - "type": "checkbox", - // data["checkbox"] being either true/false - "value": data["checkbox"], - "onPressed": { - "code": "checkbox", - } -} -``` - -### Disabled checkbox - -To disable a checkbox you only need to not use the `onPressed` property. - -```json -{ - "type": "checkbox", - // Checkbox will still be checked even if disabled when setting value to true - "value": true, -} -``` - -### Tristate checkbox - -The checkbox can be used as a tristate element. This means that in addition to being `true`/`false` it can also be `null`. This is commonly used on tables that contains multiple checkboxes, when only a part of the checkboxes are selected, the "parent" checkbox enters the third state, it is not checked because not all of its children have been selected and not unchecked beause some have been. - -```json -{ - "type": "checkbox", - // data["checkbox"] being either true/false/null - "value": data["checkbox"], - "tristate": true, - "onPressed": { - "code": "checkbox" - } -} -``` \ No newline at end of file diff --git a/src/markdown/components-api/components/container.md b/src/markdown/components-api/components/container.md deleted file mode 100644 index c4066cc..0000000 --- a/src/markdown/components-api/components/container.md +++ /dev/null @@ -1,104 +0,0 @@ -# container - -The container component that can be used to apply border, padding, constraints or decoration to its child. - -## Examples - -### Simple container - -```json -{ - "type": "container", - "child": { - "type": "text", - "value": "Text inside a container", - } -} -``` - -### Container with padding - -```json -{ - "type": "container", - "padding": { - "top": 8, - "left": 8, - "bottom": 8, - "right": 8 - }, - "child": { - "type": "text", - "value": "Text inside a container with padding" - } -} -``` - -### Container with border - -```json -{ - "type": "container", - // We use a width of 3 for each side of the border and a blue color - "border": { - "top": {"width": 3, "color": 0xFF36D8F1}, - "left": {"width": 3, "color": 0xFF36D8F1}, - "bottom": {"width": 3, "color": 0xFF36D8F1}, - "right": {"width": 3, "color": 0xFF36D8F1}, - }, - "child": { - "type": "text", - "value": "Text inside a container with border" - } -} -``` - -### Container with constraints - -```json -{ - "type": "container", - "decoration": {"color": 0xFFFF0000}, - "constraints": { - "minWidth": 100, - "maxWidth": 100, - "minHeight": 100, - "maxHeight": 100, - }, - "child": { - "type": "text", - "value": "Text inside a 100x100 container" - } -}, -``` - -### Container with decoration - -```json -{ - "type": "container", - "decoration": { - "color": 0xFFE0F7F4, - "borderRadius": { - "topLeft": { - "x": 10, - "y": 10, - }, - "bottomRight": { - "x": 10, - "y": 10, - }, - }, - "boxShadow": { - "color": 0x42000000, - "blurRadius": 3, - "spreadRadius": 3, - "offset": {"dx": 5, "dy": 2} - } - }, - "child": { - "type": "text", - "value": "Text inside a container with decoration" - } -} -``` \ No newline at end of file diff --git a/src/markdown/components-api/components/dropdownButton.md b/src/markdown/components-api/components/dropdownButton.md deleted file mode 100644 index 3d3d97b..0000000 --- a/src/markdown/components-api/components/dropdownButton.md +++ /dev/null @@ -1,70 +0,0 @@ -# dropdownButton - -A dropdown button that can be used to open a dropdown menu when clicked. - -## Examples - -### Simple dropdown button - -This creates a dropdown button that shows a button `Menu Button` when clicked. - -```json -{ - "type": "dropdownButton", - "text": "Simple", - "child": { - "type": "button", - "text": "Menu Button", - "mainStyle": "secondary", - "onPressed": {"code": "doSomething"}, - }, -} -``` - -### Disabled dropdown button - -This creates a disabled dropdownButton, this means that it cannot be opened. The `child` property is still necessary even though it is impossible to show it. - -```json -{ - "type": "dropdownButton", - "text": "Disabled dropdown", - "disabled": true, - "child": { - "type": "text", - "value": "Mandatory child", - } -} -``` - -### Sized and styled dropdown button - -```json -{ - "type": "dropdownButton", - "text": "Sized dropdown", - "size": "large", - "mainStyle": "tertiary", - "child": { - "type": "text", - "value": "Mandatory child", - } -} -``` - -### Dropdown button with icon - -```json -{ - "type": "dropdownButton", - "text": "Dropdown button with icon", - "icon": { - "type": "icon", - "value": "yard_sharp" - }, - "child": { - "type": "text", - "value": "foo", - }, -} -``` diff --git a/src/markdown/components-api/components/flex.md b/src/markdown/components-api/components/flex.md deleted file mode 100644 index 08d7b34..0000000 --- a/src/markdown/components-api/components/flex.md +++ /dev/null @@ -1,102 +0,0 @@ -# flex - -The Flex component is a container that displays its children in a row or column. - -## Examples - -### Simple flex - -The flex component lays its children horizontally by default. - -```json -{ - "type": "flex", - "children": [ - {"type": "text", "value": "First child"}, - {"type": "text", "value": "Second child"} - ] -} -``` - -### Vertical flex - -```json -{ - "type": "flex", - "direction": "vertical", - "children": [ - {"type": "text", "value": "First child"}, - {"type": "text", "value": "Second child"} - ] -} -``` - -### Flex children alignment - -The children of the flex can be aligned along and across its `direction`, the properties for that are respectively called `mainAxisAlignment` and `crossAxisAlignment`. - -```json -{ - "type": "flex", - "mainAxisAlignment": "center", - "crossAxisAlignment": "center", - "children": [ - {"type": "text", "value": "First child"}, - {"type": "text", "value": "Second child"} - ] -} -``` - -### Spacing children of flex & padding - -The children of the flex component can be spaced out by using the `spacing` property. The amount of spacing defined in this property will be factored by the current Theme's `baseSize` attribute which by default is set to 8. This means that by setting a spacing of 2, the children will be 16 units apart. - -A padding can also be added to the flex. For example if the flex is at the root of your application, you might want to add some padding to not be too close to the screen border. - -```json -{ - "type": "flex", - "spacing": 2, - "padding": { - "top": 10, - "left": 10, - "right": 10, - "bottom": 10, - }, - "children": [ - {"type": "text", "value": "First child"}, - {"type": "text", "value": "Second child"} - ] -} -``` - -### Fill parent of flex - -The flex can be set to fill its parent in the main axis. - -```json -{ - "type": "flex", - "fillParent": true, - "children": [ - {"type": "text", "value": "First child"}, - {"type": "text", "value": "Second child"} - ] -} -``` - -### Flex scroll - -Sometimes a flex can have a lot of children, this can cause the content to get out of the screen. To make sure that all of the content is readable, use the `scroll` property. - -```json -{ - "type": "flex", - "scroll": true, - "children": [ - {"type": "text", "value": "First child"}, - {"type": "text", "value": "Second child"}, - {"type": "text", "value": "Third child that has a text length that causes it to go out of the screen"} - ] -} -``` diff --git a/src/markdown/components-api/components/flexible.md b/src/markdown/components-api/components/flexible.md deleted file mode 100644 index 125a7cd..0000000 --- a/src/markdown/components-api/components/flexible.md +++ /dev/null @@ -1,64 +0,0 @@ -# flexible - -The Flexible component can be used to define the way in which its child flexes. - -## Examples - -### Simple flexible - -The default flexible behavior when only using the child property is to take the minimal amount of space. - -```json -{ - "type": "flexible", - "child": { - "type": "text", - "value": "Text in flexible" - } -} -``` - -### Flexible `flex` property - -A flexible component can use the `flex` property as a factor to determine the space to take on the screen. This makes sense when using multiple flexible components. - -```json -{ - "type": "flex", - "children": [ - { - "type": "flexible", - "flex": 1, - "child": { - "type": "text", - "value": "1/4 of the screen width" - } - }, - { - "type": "flexible", - "flex": 3, - "child": { - "type": "text", - "value": "3/4 of the screen width" - } - } - ] -} -``` - -### Flexible `fit` property - -A flexible component can use the `fit` property to decide how it will fill the available space. Using `tight` will take all of the available space while using `loose` will take a minimal amount of space. - -Flexible uses a `loose` fit by default. - -```json -{ - "type": "flexible", - "fit": "tight", - "child": { - "type": "text", - "value": "Text in flexible" - } -} -``` \ No newline at end of file diff --git a/src/markdown/components-api/components/icon.md b/src/markdown/components-api/components/icon.md deleted file mode 100644 index cef8eeb..0000000 --- a/src/markdown/components-api/components/icon.md +++ /dev/null @@ -1,27 +0,0 @@ -# icon - -The icon component. - -All of the possible values for the icon come from the material design icons. - -## Examples - -### Simple icon - -```json -{ - "type": "icon", - "value": "abc" -} -``` - -### Icon color & size - -```json -{ - "type": "icon", - "value": "abc", - "color": 0xFF000000, - "size": 100 -} -``` diff --git a/src/markdown/components-api/components/image.md b/src/markdown/components-api/components/image.md deleted file mode 100644 index cc2372a..0000000 --- a/src/markdown/components-api/components/image.md +++ /dev/null @@ -1,48 +0,0 @@ -# image - -The image component. - -This component can be used to load an image online or directly from the application. - -## Examples - -### Simple image - -```json -{ - "type": "image", - "src": "https://avatars.githubusercontent.com/u/18312505?s=200&v=4", -} -``` - -### Image with placeholders - -```json -{ - "type": "image", - "src": "https://avatars.githubusercontent.com/u/18312505?s=200&v=4", - "errorPlaceHolder": { - "type": "text", - "value": "This placeholder is shown when the image encounters an error." - }, - "framePlaceholder": { - "type": "text", - "value": "This placeholder is shown over the image as a custom frame." - }, - "loadingPlaceholder": { - "type": "text", - "value": "This placeholder is shown when the image is still loading." - } -} -``` - -### Image size - -```json -{ - "type": "image", - "src": "https://avatars.githubusercontent.com/u/18312505?s=200&v=4", - "width": 150, - "height": 150, -} -``` \ No newline at end of file diff --git a/src/markdown/components-api/components/menu.md b/src/markdown/components-api/components/menu.md deleted file mode 100644 index de2be5f..0000000 --- a/src/markdown/components-api/components/menu.md +++ /dev/null @@ -1,21 +0,0 @@ -# menu - -The menu component. - -This component is meant to be used along with the `menuItem` component because there is no real interest of using it alone. - -## Examples - -### Simple menu - -```json -{ - "type": "menu", - "children": [ - { - "type": "text", - "value": "Child" - } - ] -} -``` \ No newline at end of file diff --git a/src/markdown/components-api/components/menuItem.md b/src/markdown/components-api/components/menuItem.md deleted file mode 100644 index 73af3ba..0000000 --- a/src/markdown/components-api/components/menuItem.md +++ /dev/null @@ -1,30 +0,0 @@ -# menuItem - -This component is made to be used with the `menu` component. This will ensure that the styling is correct and that the menu items are properly sized. - -## Examples - -### Simple menuItem - -```json -{ - "type": "menuItem", - "text": "Simple menuItem", -} -``` - -### MenuItem icon & on pressed - -```json -{ - "type": "menuItem", - "text": "Menu Item", - "icon": { - "type": "icon", - "value": "abc", - }, - "onPressed": { - "action": "pressed" - } -} -``` \ No newline at end of file diff --git a/src/markdown/components-api/components/overlayEntry.md b/src/markdown/components-api/components/overlayEntry.md deleted file mode 100644 index 7dd0eb0..0000000 --- a/src/markdown/components-api/components/overlayEntry.md +++ /dev/null @@ -1,46 +0,0 @@ -# overlayEntry - -This component is used to create an **overlay** that shows itself over the current application's UI. For example, it might be interesting to use it when displaying errors in a snackbar. - -This component uses the `showOverlay` boolean property to know whether it should be shown to the screen or not. This property **should be used** to open/close the overlayEntry. The overlayEntry **should not be** directly removed from the JSON tree as it will not follow the right disposing process, the overlayEntry will end up stuck on the screen with no way to remove it. - -It is recommended to put overlays inside the root `flex` component of the application. An example is shown below. - -## Examples - -### Simple overlayEntry - -```json -{ - "type": "overlayEntry", - "showOverlay": true, - "child": { - "type": "text", - "value": "This text is shown inside of an overlayEntry." - } -} -``` - -### Recommended : Overlays in root component - -```json -{ - "type": "flex", - "children": [ - { - "type": "text", - "value": "This is the main UI of the app", - }, - // Add overlays in root flex component. - { - "type": "overlayEntry", - "showOverlay": true, - "child": { - "type": "text", - "value": "This text is shown inside of an overlayEntry." - } - } - ] -} -``` - diff --git a/src/markdown/components-api/components/radio.md b/src/markdown/components-api/components/radio.md deleted file mode 100644 index e677248..0000000 --- a/src/markdown/components-api/components/radio.md +++ /dev/null @@ -1,40 +0,0 @@ -# radio - -This component is basically a radio component as you might know it from other frameworks. - -The radio component uses the `value` and `groupValue` properties to know whether it should be checked or not. The `value` property holds the value of the radio component and the `groupValue` property holds the current selected radio value for a group of radios. - -## Examples - -### Basic selected radio - -```json -{ - "type": "radio", - "value": "a", - "groupValue": "a", -} -``` - -### Group of radios - -```json -{ - "type": "flex", - "children": [ - { - "type": "radio", - // This radio will be selected because the groupValue matches the value - "value": "a", - "groupValue": "a", - }, - { - "type": "radio", - // This radio will not be selected because the groupValue does not match the value - "value": "b", - "groupValue": "a", - }, - ] -} - -``` \ No newline at end of file diff --git a/src/markdown/components-api/components/slider.md b/src/markdown/components-api/components/slider.md deleted file mode 100644 index 8f7af15..0000000 --- a/src/markdown/components-api/components/slider.md +++ /dev/null @@ -1,27 +0,0 @@ -# slider - -The slider component can be used to select from a **range** of values. - -A certain amount of **divisions** can be set for the slider using the `divisions` property. Then, the `min` and `max` properties need to be set to define the values of the slider. For example, in the case of a slider ranging from **1 to 10** the number of divisions would be 10 and the **min** and **max** properties would be respectively set to 1 and 10. - -This component defines 3 types of listener. `onChanged` that is called when the value of the slider changes. `onChangeStart` called when the handle of the slider is pressed. `onChangeEnd` called when the handle of the slider is released. - -## Examples - -### Slider ranging from 1 to 10 - -For this example, the data should contain the `slider` value and the `onChanged` listener should properly change that value. - -```json -{ - "type": "slider", - "value": data["slider"], - "divisions": 10, - "label": data["slider"], - "min": 1, - "max": 10, - "onChanged": { - "code": "changeSliderValue", - } -} -``` \ No newline at end of file diff --git a/src/markdown/components-api/components/stack.md b/src/markdown/components-api/components/stack.md deleted file mode 100644 index 025760c..0000000 --- a/src/markdown/components-api/components/stack.md +++ /dev/null @@ -1,40 +0,0 @@ -# stack - -This component is used to make its children overlap. This is particularly interesting when building complex widgets that needs to put some content above an image or for styling some widgets. -The first child in the list will be in the background while the last child of the list will be in the foreground of the stack. - -## Examples - -### Stacking containers - -This example will show two boxes, a red one at the background and a green one in the foreground. Both are respectively sized 100x100 and 60x60 so that they can be seen. - -```json -{ - "type": "stack", - "children": [ - { - "type": "container", - "decoration": {"color": 0xFFFF0000}, - "constraints": { - "minWidth": 100, - "maxWidth": 100, - "minHeight": 100, - "maxHeight": 100, - }, - "child": {"type": "text", "value": "This is in the background"} - }, - { - "type": "container", - "decoration": {"color": 0xFF00FF00}, - "constraints": { - "minWidth": 60, - "maxWidth": 60, - "minHeight": 60, - "maxHeight": 60, - }, - "child": {"type": "text", "value": "This is in the foreground."} - }, - ] -} -``` \ No newline at end of file diff --git a/src/markdown/components-api/components/statusSticker.md b/src/markdown/components-api/components/statusSticker.md deleted file mode 100644 index 3b021a7..0000000 --- a/src/markdown/components-api/components/statusSticker.md +++ /dev/null @@ -1,14 +0,0 @@ -# statusSticker - -This component is a status based sticker. It can take 4 different statuses, `success`, `warning`, `error` and `pending` - -## Examples - -### Simple Status Sticker - -```json -{ - "type": "statusSticker", - "status": "success" -} -``` \ No newline at end of file diff --git a/src/markdown/components-api/components/text.md b/src/markdown/components-api/components/text.md deleted file mode 100644 index 7ddf39e..0000000 --- a/src/markdown/components-api/components/text.md +++ /dev/null @@ -1,51 +0,0 @@ -# text - -This is a typical text component. - -This component has a `children` property in case some complex text needs to be done (see examples below). - -## Examples - -### Basic text - -```json -{ - "type": "text", - "value": "This is a text.", -} -``` - -### Complex text - -This is an example involving a complex text. It is composed of bold and italic values all in one sentence. - -```json -{ - "type": "text", - "value": "This is a text involving", - "children": [ - { - "type": "text", - "value": "bold", - "style": { - "fontWeight": "bold", - }, - }, - { - "type": "text", - "value": "and", - }, - { - "type": "text", - "value": "italic", - "style": { - "fontStyle": "italic", - }, - }, - { - "type": "text", - "value": "values.", - }, - ] -} -``` \ No newline at end of file diff --git a/src/markdown/components-api/components/textfield.md b/src/markdown/components-api/components/textfield.md deleted file mode 100644 index 71ceb4c..0000000 --- a/src/markdown/components-api/components/textfield.md +++ /dev/null @@ -1,51 +0,0 @@ -# textfield - -This component gives the possibility to the user to write text. - -## Examples - -### Basic textfield - -```json -{ - "type": "textfield", - "value": "fixedValue", - "onChanged": { - // This listener cannot change the value because the value is fixed to "fixedValue" - "action": "changeValue" - } -} -``` - - -### Complex textfield - -```json -{ - "type": "textfield", - // The value is stored in the app data - "value": data, - "style": { - "decoration": { - "icon": { - "type": "icon", - "value": "star", - }, - "filled": true, - "fillColor": 0xFFBBDEFB, - "border": { - "type": "outline", - }, - "hintText": "hint", - "helperText": "helper", - "labelText": "label", - // Showing the string length under the textfield - "counterText": "${data.length}" - }, - }, - "minLines": 3, - "maxLines": 5, - // This listener takes care of changing the value in the app data - "onChanged": {"code": "CodeA"} -}, -``` \ No newline at end of file diff --git a/src/markdown/components-api/components/toggle.md b/src/markdown/components-api/components/toggle.md deleted file mode 100644 index 48043f0..0000000 --- a/src/markdown/components-api/components/toggle.md +++ /dev/null @@ -1,19 +0,0 @@ -# toggle - -This component can be toggled on or off. - -## Examples - -### Simple toggle - -```json -{ - "type": "toggle", - // The value is stored in the app data - "value": data, - "onPressed": { - // This listener takes care of toggling the value of this toggle component. - "code": "toggle", - } -}, -``` \ No newline at end of file diff --git a/src/markdown/components-api/components/widget.md b/src/markdown/components-api/components/widget.md deleted file mode 100644 index 40931fc..0000000 --- a/src/markdown/components-api/components/widget.md +++ /dev/null @@ -1,55 +0,0 @@ -# widget - -This component calls a widget defined in the application. This can be used to create reusable widgets across the application and call them from anywhere. - -The widget must be defined and references in the index.js root file of the application. - -## Examples - -### Simple widget - -Create a file that will contain the widget. - -```json -module.exports = (users, _props) => { - return { - "type": "text", - "value": "This is a widget", - }; -} -``` - -Reference this widget in the root index.js of the application. - -```json -module.exports = async () => { - return { - widgets: { - myWidget: require('./widgets/myWidget.js'), - }, - listeners: {}, - rootWidget: 'main' - } -} -``` - -Then instantiate the widget in the root widget of the application. - -```json -module.exports = (users, _props) => { - return { - "type": "flex", - "children": [ - { - "type": "text", - "value": "This the root widget", - }, - // Will call myWidget and show the "This is a widget" text - { - "type": "widget", - "name": "myWidget", - } - ] - }; -} -``` \ No newline at end of file diff --git a/src/markdown/components-api/components/wrap.md b/src/markdown/components-api/components/wrap.md deleted file mode 100644 index 02e2591..0000000 --- a/src/markdown/components-api/components/wrap.md +++ /dev/null @@ -1,25 +0,0 @@ -# wrap - -This component can be used to display a long list of components, if they get out of the screen the wrap component will make the overflowing ones use a new row. - -## Examples - -### Simple wrap - -```json -{ - "type": "wrap", - "spacing": 10, - "children": [ - { - "type": "text", - "value": "Foo", - }, - // This text will be wrapped, this means that it will show itself right under the "Foo" text instead of out of the screen - { - "type": "text", - "value": "Wrapped", - } - ] -} -``` \ No newline at end of file diff --git a/src/markdown/getting-started.md b/src/markdown/getting-started.md index ea4ade9..89ce134 100644 --- a/src/markdown/getting-started.md +++ b/src/markdown/getting-started.md @@ -8,14 +8,14 @@ To properly run the commands through this tutorial, you will need to install the - Docker Compose - Lenra CLI -For installation instructions, you can directly check the CLI repository : https://github.com/lenra-io/lenra_cli. +For installation instructions, you can directly check the CLI repository : [https://github.com/lenra-io/lenra_cli](https://github.com/lenra-io/lenra_cli). ## How to start a new Lenra project It is very easy to start a new Lenra project. The most important thing for you is to choose your favorite programming language with which you will be developing your application. -Choose one in [the template list](https://github.com/orgs/lenra-io/repositories?q=&type=template&language=&sort=stargazers) that will be used as a starting point for your application. +Choose one in [the template list](https://github.com/orgs/lenra-io/repositories?q=&type=template&language=&sort=stargazers) that will be used as a starting point for your application. -You then need to run the following command using the Lenra CLI to create your new project using the template of your choice. +You then need to run the following command using the Lenra CLI to create your new project using the template of your choice. See the next example using the **template-node** template: ```console @@ -50,6 +50,6 @@ After successfully completing this step you will be redirected to the creation o ![New Lenra project](./img/new-lenra-project.png) -Then you will have to click `Publish my application` at the top right corner, your application will be sent to Lenra's servers and deployed to be accessible directly for the Lenra Store. -Once your application is fully deployed on our servers, it will be accessible by clicking the `See my application` button. -You can share this application by changing its visibility to `public` in the settings and sending the link to people (which should look close to app.lenra.io/#/app/f6279d6a-3b71-4520-a7f8-0f7b28700de9). \ No newline at end of file +Then you will have to click `Publish my application` at the top right corner, your application will be sent to Lenra's servers and deployed to be accessible directly for the Lenra Store. +Once your application is fully deployed on our servers, it will be accessible by clicking the `See my application` button. +You can share this application by changing its visibility to `public` in the settings and sending the link to people (which should look close to app.lenra.io/#/app/f6279d6a-3b71-4520-a7f8-0f7b28700de9). diff --git a/src/server.js b/src/server.js index 939e214..a80f54d 100644 --- a/src/server.js +++ b/src/server.js @@ -15,9 +15,10 @@ app.get('/(*/)?(*.html)?', async (req, res) => { const Views = require('./utils/views'); const common = require('./i18n/common.json'); const lang = req.query.lang || languagePriority[0]; - console.log(req.path, lang); const pTranslation = Translations.loadTranslation(lang); + const pages = Views.translatePages(await Views.getPages(), await pTranslation); + const currentPage = Views.getPageFromPath(pages, req.path); const file = currentPage ? Views.getViewFile(currentPage.view, lang) : null; if (!file) { @@ -25,8 +26,8 @@ app.get('/(*/)?(*.html)?', async (req, res) => { return; } const props = { - ...common, - componentsApiBasePath: Views.componentsApiBasePath, + ...common, + componentsApiBasePath: Views.componentsApiBasePath, language: lang, pages, currentPage @@ -39,4 +40,4 @@ app.use(express.static(staticPath, {dotfiles: 'allow'})); app.listen(port, () => { console.log(`Example app listening at http://localhost:${port}`); -}) \ No newline at end of file +}) diff --git a/src/static/js/main.js b/src/static/js/main.js index b2088e1..119560f 100644 --- a/src/static/js/main.js +++ b/src/static/js/main.js @@ -1,6 +1,5 @@ // ajouter/générer le style sans les variables si elles ne sont pas gérées var computedStyle = getComputedStyle(document.querySelector("a.primary"), null); -console.log(computedStyle.backgroundColor); if (computedStyle.backgroundColor=="transparent") { console.log("Variables non gérées"); var script = document.createElement("script"); @@ -23,4 +22,4 @@ function checkReady() { document.body.classList.add('ready'); } -checkReady(); \ No newline at end of file +checkReady(); diff --git a/src/utils/common.js b/src/utils/common.js index 1209fa5..cbd3698 100644 --- a/src/utils/common.js +++ b/src/utils/common.js @@ -58,4 +58,4 @@ function getFilesRecursively(dir) { module.exports = { mergeDeep, getFilesRecursively -} \ No newline at end of file +} diff --git a/src/utils/components.js b/src/utils/components.js deleted file mode 100644 index 794fc8e..0000000 --- a/src/utils/components.js +++ /dev/null @@ -1,273 +0,0 @@ -const fs = require('fs-extra'); -const path = require('path'); -const { getFilesRecursively } = require('./common'); - -const compFileRegex = /^(.+[.]json)$/; -const componentsPath = path.join(__dirname, '..', 'api/api'); -const subDefinitionSeparator = '@'; - -/** - * Get the managed languages - * @returns {Promise>} - */ -function getManagedComponents() { - return getFilesRecursively(componentsPath) - .map(file => file.match(compFileRegex)) - .filter(match => match) - .map(match => match[1]) - .map(file => path.relative(componentsPath, file).replace('\\', '/')); -} - -/** - * Load all the translations managed - * @returns - */ -async function loadComponents() { - const components = {}; - await Promise.all((getManagedComponents()) - .map(comp => loadComponent(comp) - .then(c => components[comp] = c))); - return components; -} - -/** - * Load the defined translations for a language - * @param {string} comp The translations language - * @returns The translation map - */ -async function loadComponent(comp) { - return fs.readFile(path.join(componentsPath, `${comp}`), 'utf8') - .then(data => { - return JSON.parse(data); - }); -} - -const classicProps = ['$schema', '$id', 'title', 'additionalProperties', '$ref', 'type', 'description', 'enum', 'default', 'items']; - -/** - * Compare two strings - * @param {string} a - * @param {string} b - * @returns {number} - */ -function compareStrings(a, b) { - a = a.toLowerCase(); - b = b.toLowerCase(); - if (a < b) return -1; - if (a > b) return 1; - return 0; -} - -/** - * Parse API schemata to definitions - * @param {object} schemata Components schemata - * @param {Definition} parent Parent definition - * @returns {Definition[]} - */ -function parseSchemata(schemata, parent) { - return Object.entries(schemata) - .filter(([id, schema]) => id.endsWith('/iconData.schema.json') || schema.properties) - .sort(([aId, a], [bId, b]) => { - const aParts = aId.split('/'); - const bParts = bId.split('/'); - const size = Math.min(aParts.length, bParts.length) - 1; - for (let i = 0; i < size; i++) { - const diff = compareStrings(aParts[i], bParts[i]); - if (diff!=0) return diff; - } - if (aParts.length!=bParts.length) - return aParts.length - bParts.length; - return compareStrings(aParts[size], bParts[size]); - }) - .map(([id, schema]) => { - const ret = parseSchema(id, schema); - if (schema.definitions) - ret.setSubDefinitions(parseSchemata(schema.definitions, ret)); - return ret; - }); - - /** - * Parse the schema to a definition - * @param {string} id Schema id - * @param {object} schema The schema - * @param {Definition} parent The parent definition - * @return {Definition} - */ - function parseSchema(id, schema) { - const properties = parseProperties(schema.properties); - const title = schema.title || id; - return new Definition( - parent ? `${parent.id}@${id}` : id, - parent ? `${parent.name}@${title}` : title, - schema.description, - properties ? null : parseType(schema), - properties - ); - - - function parseType(property) { - let content = property; - let isArray = content.type=='array'; - let description = content.description; - if (isArray) { - content = {...content, ...property.items}; - if (!description) description = property.items.description; - } - let ref = content['$ref']; - if (ref) { - let target = schemata[ref]; - if (ref.startsWith('#/')) { - ref = ref.substring(2); - const parts = ref.split('/'); - target = parts.reduce((o, att) => o[att], schema); - const n = parts[parts.length-1]; - ref = (parent ? parent.id : id) + subDefinitionSeparator + n; - content.type = ref; - } - else { - content.type = target.title; - } - if (!description) description = target.description; - if (!target.properties && !ref.endsWith('/iconData.schema.json')) { - ref = null; - content = {...content, ...target}; - } - } - return new Type(content.type, description, ref, content, isArray, content.enum, content.default); - } - /** - * @param {string} name - * @param {any} property - * @returns - */ - function parseProperty(name, property) { - const type = parseType(property); - const attributes = Object.entries(type.target) - .map(([key, value]) => ({key, value})) - .filter(({key, value}) => !classicProps.includes(key) && value!=null) - .flatMap(({key, value}) => { - if (key=='properties') - return parseProperties(value) - .map(p => new Attribute(`${key}.${p.name}`, p)); - return [new Attribute(key, value)]; - }); - return new Property( - name, - property.description || type.description, - type, - schema.required && schema.required.includes(name), - attributes - ); - } - - /** - * @param {any} properties Properties schema - * @returns {Property[]} - */ - function parseProperties(properties) { - if (!properties) return null; - return Object.entries(properties) - .map(([name, property]) => parseProperty(name, property)) - .sort((a, b) => { - if (a.name=='type') - return -1; - if (b.name=='type') - return 1; - if (a.required && !b.required) - return -1; - else if (!a.required && b.required) - return 1; - return compareStrings(a.name, b.name); - }); - } - } -} - -class Definition { - /** - * @param {string} id The definition id - * @param {string} name The definition name - * @param {string} description The definition description - * @param {Type} type The definition type - * @param {Property[]} properties The definition properties - */ - constructor(id, name, description, type, properties) { - this.id = id; - this.name = name; - this.description = description; - this.type = type; - this.properties = properties; - this.hasOtherAttributes = properties && properties.some(p => p.attributes.length>0); - this.setSubDefinitions([]); - } - - /** - * @param {Definition[]} subDefs - */ - setSubDefinitions(subDefs) { - this.subDefinitions = subDefs; - this.subDefinitionsMap = {}; - subDefs.forEach(d => this.subDefinitionsMap[d.id] = d); - } -} - -class Property { - /** - * @param {string} name Property name - * @param {string} description Property description - * @param {Type} type Property type - * @param {boolean} required True if the property is required - * @param {Attribute[]} attributes The property other attributes - */ - constructor(name, description, type, required, attributes) { - this.name = name; - this.description = description; - this.type = type; - this.required = required; - this.attributes = attributes; - } -} - -class Type { - /** - * @param {string} name - * @param {string} description - * @param {string} ref - * @param {any} target - * @param {boolean} isArray - * @param {any[]} values - * @param {any} defaultValue - */ - constructor(name, description, ref, target, isArray, values, defaultValue) { - this.name = name; - this.description = description; - this.ref = ref; - this.target = target - this.isArray = isArray; - this.values = values; - this.defaultValue = defaultValue; - } -} - -class Attribute { - /** - * @param {string} name Attribute name - * @param {any} value Attribute value - */ - constructor(name, value) { - this.name = name; - this.value = value; - } -} - -module.exports = { - Definition, - Property, - Type, - Attribute, - getManagedComponents, - loadComponent, - loadComponents, - parseSchemata, - subDefinitionSeparator -} \ No newline at end of file diff --git a/src/utils/views.js b/src/utils/views.js index a267607..2c77368 100644 --- a/src/utils/views.js +++ b/src/utils/views.js @@ -1,15 +1,17 @@ const fs = require('fs-extra'); const Path = require('path'); -const Components = require('./components'); const Utils = require('./common'); const showdown = require('showdown'); +const glob = require('glob'); +const config = require('../i18n/common.json'); +const { file } = require('jszip'); const converter = new showdown.Converter(); + const viewsPath = Path.join(__dirname, '..', 'views'); const markdownPath = Path.join(__dirname, '..', 'markdown'); const componentsApiBasePath = '/components-api/'; -// const componentsApiBasePath = '/'; /** * Get the view file for the given path and language @@ -18,145 +20,139 @@ const componentsApiBasePath = '/components-api/'; * @returns {string?} */ function getViewFile(path, lang) { - let p; - if (lang) { - p = Path.join(viewsPath, `${path}.${lang}.pug`); - if (fs.existsSync(p)) return p; - } - p = Path.join(viewsPath, `${path}.pug`); - if (fs.existsSync(p)) return p; - return null; + let p; + if (lang) { + p = Path.join(viewsPath, `${path}.${lang}.pug`); + if (fs.existsSync(p)) return p; + } + p = Path.join(viewsPath, `${path}.pug`); + if (fs.existsSync(p)) return p; + return null; } -/** - * @returns - */ async function getPages() { - const home = new Page('/', 'Documentation', "Lenra's documentation", 'layout'); - home.collapsable = true; - const componentsApi = new Page(componentsApiBasePath, 'Components API', "The Lenra's components API references. Understand the UI creation with Lenra", 'definition-summary'); - const componentsPromise = Components.loadComponents(); - const markdowns = Utils.getFilesRecursively(markdownPath); - componentsApi.subPages = generateDefinitionPages(Components.parseSchemata(await componentsPromise)); - componentsApi.collapsable = false; - home.subPages = [componentsApi]; - return includeMarkdownPages([home], markdowns); -} + const markdowns = Utils.getFilesRecursively(markdownPath); + const home_markdown = markdowns.splice(markdowns.findIndex(page => Path.basename(page, '.md') === 'index'), 1)[0] + const home = new Page('/', 'Documentation', "Lenra's documentation", 'layout'); + home.content = converter.makeHtml(fs.readFileSync(home_markdown, 'utf8')); + home.collapsable = true; -/** - * - * @param {Page[]} pages - * @param {string[]} markdowns - */ -function includeMarkdownPages(pages, markdowns) { - markdowns - .map(path => ({path, pagePath: '/'+Path.relative(markdownPath, path)})) - // TODO: filter language specific files - .map(({path, pagePath}) => ({path, pagePath: pagePath.replace(/\.md$/, '.html').replace(/\/index\.html$/, '/')})) - .forEach(({path, pagePath}) => { - let parentPath = pagePath.endsWith('/') ? pagePath.replace(/\/$/, '') : pagePath; - parentPath = parentPath.substring(0, pagePath.lastIndexOf('/') + 1); - const parentPage = parentPath ? getPageFromPath(pages, parentPath) : null; - const findInPages = parentPage ? parentPage.subPages : pages; - let page = getPageFromPath(findInPages, pagePath); - if (!page) { - let pageName = pagePath.endsWith('/') ? pagePath.replace(/\/$/, '') : pagePath.replace(/\.html$/, ''); - pageName = pageName.substring(pageName.lastIndexOf('/') + 1); - page = new Page(pagePath, pageName, "Description is not managed yet", "layout"); - parentPage.subPages.push(page); - } - page.markdown = converter.makeHtml(fs.readFileSync(path, 'utf8')); - }); - return pages; + const static_pages = markdowns + .filter(file => file.endsWith('.md')) + .map(file => { + const filename = Path.basename(file, '.md') + const page = new Page(file.substring(file.lastIndexOf('/')).replace('.md', '.html'), + filename.charAt(0).toUpperCase() + filename.substring(1), + "Description is not managed yet", + "layout"); + page.content = converter.makeHtml(fs.readFileSync(file, 'utf8')); + const position = config.page[page.path]?.position + if (position !== undefined) { + home.subPages.splice(position, 0, page) + } else { + home.subPages.push(page) + } + return page + }); + + let all_pages = [home, ...static_pages]; + + const api_dir = Path.join(__dirname, '../api/', componentsApiBasePath) + if ((await fs.stat(api_dir)).isDirectory()) { + home.subPages = home.subPages.concat(await Promise.all(fs.readdirSync(api_dir).filter(api_name => !api_name.startsWith('.')).map(async api_name => { + const api_path = Path.join(api_dir, api_name) + const api = new Page(`/${api_name}/`, api_name.charAt(0).toUpperCase() + api_name.substring(1), 'Description is not managed yet', 'definition-summary'); + api.collapsable = true + + const components_paths = await new Promise((resolve, reject) => + glob( + Path.join(api_path, '*.html'), + (error, match) => error ? reject(error) : resolve(match) + ) + ).catch(err => { + console.error(err); + }); + api.subPages = components_paths + .map(component_path => { + const filename = Path.basename(component_path, '.html') + return new Page( + Path.join(api.path, Path.basename(component_path)), + filename.charAt(0).toUpperCase() + filename.substring(1), + 'Description is not managed yet', + 'definition', + fs.readFileSync(component_path).toString()) + }); + all_pages = [...all_pages, api, ...api.subPages] + return api; + })).catch(err => { + console.error(err) + })); + } else { + console.error(`${api_dir} is not a correct directory. Make sure it exists.`) + } + return all_pages; } /** - * @param {Page[]} pages - * @param {string} path + * @param {Page[]} pages + * @param {string} path * @returns {Page} */ function getPageFromPath(pages, path) { - const p = pages.find(p => path.startsWith(p.path)); - if (!p || p.path==path) return p; - if (p.subPages.length) - return getPageFromPath(p.subPages, path); - return p; + const p = pages.find(p => path.startsWith(p.path)); + if (!p || p.path==path) return p; + if (p.subPages.length) + return getPageFromPath(p.subPages, path); + return p; } /** - * Generates a map of components pages from the components ids - * @param {Definition[]} definitions The components definitions - * @returns - */ -function generateDefinitionPages(definitions) { - const pages = []; - const nodes = {}; - definitions.forEach(def => { - let pos = def.id.indexOf('/'); - let parent = pages; - while (pos!=-1) { - const path = def.id.substring(0, ++pos); - if (!(path in nodes)) { - let name = path.substring(0, pos-1); - name = name.substring(name.lastIndexOf('/')+1); - pages.push(nodes[path] = new Page(componentsApiBasePath+path, name, null, 'definition-summary')); - } - parent = nodes[path].subPages; - pos = def.id.indexOf('/', pos); - } - parent.push(new Page(componentsApiBasePath+def.id.replace(/\.schema\.json$/, '.html'), def.name, def.description, 'definition', def)); - }); - return pages; -} - -/** - * @param {Page[]} pages - * @param {any} translations + * @param {Page[]} pages + * @param {any} translations */ function translatePages(pages, translations) { - const repositionPages = []; - let retPages = pages.map(page => { - const translation = translations.page[page.path] || {}; - let ret = Utils.mergeDeep({}, page, translation); - if (page.subPages.length) ret.subPages = translatePages(ret.subPages, translations); - if ('position' in ret) repositionPages.push(ret); - return ret; - }); - repositionPages.forEach(p => { - let pos = retPages.indexOf(p); - if (pos!=p.position) { - retPages.splice(pos, 1); - retPages.splice(p.position, 0, p); - } - }); - return retPages; + const repositionPages = []; + let retPages = pages.map(page => { + const translation = translations.page[page.path] || {}; + let ret = Utils.mergeDeep({}, page, translation); + if (page.subPages?.length) ret.subPages = translatePages(ret.subPages, translations); + if ('position' in ret) repositionPages.push(ret); + return ret; + }); + repositionPages.forEach(p => { + let pos = retPages.indexOf(p); + if (pos!=p.position) { + retPages.splice(pos, 1); + retPages.splice(p.position, 0, p); + } + }); + return retPages; } class Page { - /** - * @param {string} path - * @param {string} name - * @param {string} description - * @param {string} view - * @param {Definition} definition - */ - constructor(path, name, description, view, definition) { - this.path = path; - this.name = name; - this.description = description; - this.view = view; - this.definition = definition; - this.collapsable = true; - this.markdown = null; - this.subPages = []; - } + /** + * @param {string} path + * @param {string} name + * @param {string} description + * @param {string} view + * @param {Definition} definition + */ + constructor(path, name, description, view, content = null) { + this.path = path; + this.name = name; + this.description = description; + this.view = view; + this.collapsable = false; + this.content = content; + this.subPages = []; + } } module.exports = { - Page, - getViewFile, - getPages, - getPageFromPath, - translatePages, - componentsApiBasePath -} \ No newline at end of file + Page, + getViewFile, + getPages, + getPageFromPath, + translatePages, + componentsApiBasePath +} diff --git a/src/views/definition-summary.pug b/src/views/definition-summary.pug index 018eb93..a0d147c 100644 --- a/src/views/definition-summary.pug +++ b/src/views/definition-summary.pug @@ -13,6 +13,6 @@ mixin createSummaryItems(pages) li +createSummaryItem(page) -append content +append content h2 Definitions +createSummaryItems(currentPage.subPages) diff --git a/src/views/definition.pug b/src/views/definition.pug index 13fe522..a131f02 100644 --- a/src/views/definition.pug +++ b/src/views/definition.pug @@ -1,114 +1 @@ extends layout.pug - -mixin createFirstLine(hasOther) - tr - th Attribute - th Description - th Type - if (hasOther) - th(colspan=2) Details - -mixin displayTypeValue(value) - - var type = typeof value - if (type == 'string') - | "#{value}" - else if (type == 'number') - | #{value} - else - - console.log('Not managed value type', value, type) - | #{value} - -mixin displayType(definition, type) - - var name = type.name - if (!name) - if (type.values) - - name = typeof type.values[0] - each val in type.values - if (name != typeof val) - - name = 'any' - else if (type.defaultValue) - - name = typeof type.defaultValue - if (type.ref) - if (name.includes('@')) - - name = definition.subDefinitionsMap[type.ref].name - a(href=componentsApiBasePath+type.ref.replace(/\.schema.json/, '.html'))= name - else - | #{name} - if (type.isArray) - | [] - if (type.values || type.defaultValue) - | - | ( - if (type.values) - each val,i in type.values - if (i>0) - | , - | - if (val==type.defaultValue || type.values.length==1) - strong - +displayTypeValue(val) - else - +displayTypeValue(val) - else - strong - +displayTypeValue(type.defaultValue) - | ) - -mixin displayAttribute(definition, att) - if (att.name.startsWith('properties.')) - td= att.value.name - td - +displayType(definition, att.value.type) - else - td= att.name - td= att.value - -mixin createPropertyLine(definition, property) - - var rowspan = property.attributes.length > 1 ? property.attributes.length : null - tr(class=property.required ? 'required' : null) - td(rowspan=rowspan)= property.name - td(rowspan=rowspan)= property.description - td(rowspan=rowspan) - +displayType(definition, property.type) - if (property.attributes.length) - each att,i in property.attributes - if (i>0) - tr - +displayAttribute(definition, att) - else - +displayAttribute(definition, att) - else if (definition.hasOtherAttributes) - td - td - -mixin createPropertiesTable(definition) - table - +createFirstLine(definition.hasOtherAttributes) - each prop in definition.properties - +createPropertyLine(definition, prop) - - -mixin displayIcon(definition) - h2 Values - ul - each val in definition.type.values - li= val - -prepend content - - var def = currentPage.definition - if (def.description) - p= def.description - -append content - if (def.id.endsWith('/iconData.schema.json')) - +displayIcon(def) - else - if (def.properties) - h2 Properties - +createPropertiesTable(def) - each subDef in def.subDefinitions - h2(id=componentsApiBasePath+subDef.id)= subDef.name - if (subDef.description) - p= subDef.description - if (subDef.properties) - +createPropertiesTable(subDef) \ No newline at end of file diff --git a/src/views/layout.pug b/src/views/layout.pug index 27883ab..87b5437 100644 --- a/src/views/layout.pug +++ b/src/views/layout.pug @@ -16,7 +16,7 @@ html(lang=language) link(rel="icon" type="image/png" href="/favicon.png") link(rel="apple-touch-icon" href="/img/appicon.png") link(rel='stylesheet' as='style' type='text/css' href='/css/main.css' media='print' onload='this.media="all"') - noscript + noscript link(rel='stylesheet' as='style' type='text/css' href='/css/main.css') script(src='/js/main.js' defer) script(src="https://analytics.lenra.io/js/plausible.js" data-domain="!DOMAIN!" defer) @@ -29,5 +29,5 @@ html(lang=language) a(href="https://dev.lenra.io/#/register" class="button primary") Register on Lenra main block content - if(currentPage.markdown) - | !{currentPage.markdown} + if(currentPage.content) + | !{currentPage.content} diff --git a/src/views/menu.pug b/src/views/menu.pug index 9778290..de75282 100644 --- a/src/views/menu.pug +++ b/src/views/menu.pug @@ -3,7 +3,7 @@ mixin manageCollapse(collapsable) details(open) summary Open/Close block - else + else block @@ -23,4 +23,4 @@ mixin createMenuItems(pages) nav button.tertiary(onclick="this.parentNode.className = this.parentNode.className ? '' : 'opened'") Open/Close Menu a(href="https://www.lenra.io/") Lenra - +createMenuItems(pages[0].subPages) \ No newline at end of file + +createMenuItems(pages[0].subPages) diff --git a/version b/version deleted file mode 100644 index afaf360..0000000 --- a/version +++ /dev/null @@ -1 +0,0 @@ -1.0.0 \ No newline at end of file