diff --git a/packages/ui5-task-copyright/.gitignore b/packages/ui5-task-copyright/.gitignore new file mode 100644 index 000000000..6fdb5e183 --- /dev/null +++ b/packages/ui5-task-copyright/.gitignore @@ -0,0 +1,2 @@ +test/__dist__/* +!test/__dist__/.keepMe diff --git a/packages/ui5-task-copyright/.npmignore b/packages/ui5-task-copyright/.npmignore new file mode 100644 index 000000000..9daeafb98 --- /dev/null +++ b/packages/ui5-task-copyright/.npmignore @@ -0,0 +1 @@ +test diff --git a/packages/ui5-task-copyright/README.md b/packages/ui5-task-copyright/README.md new file mode 100644 index 000000000..af4fce789 --- /dev/null +++ b/packages/ui5-task-copyright/README.md @@ -0,0 +1,69 @@ +# UI5 task for appending copyright headers for TypeScript, JavaScript and XML files + +> :wave: This is a **community project** and there is no official support for this package! Feel free to use it, open issues, contribute, and help answering questions. + +Task for [ui5-builder](https://github.com/SAP/ui5-builder) for appending copyright headers to every TypeScript (`*.ts`), JavaScript (`*.js`), or XML (`*.xml`) source file. + +## Prerequisites + +- Requires at least [`@ui5/cli@3.0.0`](https://sap.github.io/ui5-tooling/v3/pages/CLI/) (to support [`specVersion: "3.0"`](https://sap.github.io/ui5-tooling/pages/Configuration/#specification-version-30)) + +> :warning: **UI5 Tooling Compatibility** +> All releases of this tooling extension using the major version `3` require UI5 Tooling V3. Any previous releases below major version `3` (if available) also support older versions of the UI5 Tooling. But the usage of the latest UI5 Tooling is strongly recommended! + +## Install + +```bash +npm install ui5-task-copyright --save-dev +``` + +## Configuration options (in `$yourapp/ui5.yaml`) + +- copyright: `String` + the value of the copyright or the path to a file containing the copyright statement - if not given, the task will be skipped + +- copyrightPlaceholder: `String` (defaults to: `copyright`) + the name of the copyright placeholder to check for in the copyright comments and to replace with the copyright value (will replace all hits of `${copyright}` or `@copyright@`) + +- currentYearPlaceholder: `String` (defaults to: `currentYear`) + the name of the currentYear placeholder in the copyright comments which will be replaced with the currentYear value (will replace all hits of `${currentYear}` or `@currentYear@`) + +- excludePatterns: `Array` + array of paths inside `$yourapp/` to exclude from the minification, e.g. 3-rd party libs in `lib/*`. defaults to an empty array `[]`. + +## Usage + +1. Define the dependency in `$yourapp/package.json`: + +```json +"devDependencies": { + // ... + "ui5-task-copyright": "*" + // ... +} +``` + +2. configure it in `$yourapp/ui5.yaml`: + +```yaml +builder: + customTasks: + - name: ui5-task-copyright + beforeTask: replaceCopyright + configuration: + copyright: "Copyright ${currentYear} UI5 Community" + excludePatterns: + - "thirdparty/" +``` + +## How to obtain support + +Please use the GitHub bug tracking system to post questions, bug reports or to create pull requests. + +## Contributing + +Any type of contribution (code contributions, pull requests, issues) to this showcase will be equally appreciated. + +## License + +This work is [dual-licensed](../../LICENSE) under Apache 2.0 and the Derived Beer-ware License. The official license will be Apache 2.0 but finally you can choose between one of them if you use this work. diff --git a/packages/ui5-task-copyright/bin/uses-ui5-task-copyright.js b/packages/ui5-task-copyright/bin/uses-ui5-task-copyright.js new file mode 100755 index 000000000..ff3ce3fc5 --- /dev/null +++ b/packages/ui5-task-copyright/bin/uses-ui5-task-copyright.js @@ -0,0 +1,31 @@ +#!/usr/bin/env node + +const path = require("path"); +const fs = require("fs"); +const yaml = require("js-yaml"); + +const ui5YamlPath = process.argv[2] || path.join(process.cwd(), "ui5.yaml"); +if (!ui5YamlPath) { + console.error("\x1b[31m[ERROR]\x1b[0m No ui5.yaml provided!"); + process.exit(1); +} +if (!fs.existsSync(ui5YamlPath) || !fs.statSync(ui5YamlPath).isFile()) { + console.error(`\x1b[31m[ERROR]\x1b[0m The ${ui5YamlPath} does not exist!`); + process.exit(1); +} + +try { + const content = fs.readFileSync(ui5YamlPath, "utf-8"); + const ui5Configs = yaml.loadAll(content); + const notFound = (ui5Configs[0]?.builder?.customTasks?.findIndex((task) => task.name === "ui5-task-copyright") || -1) === -1; + if (notFound) { + console.error(`\x1b[31m[ERROR]\x1b[0m ui5-task-copyright is not registered in ${ui5YamlPath}!`); + process.exit(1); + } +} catch (err) { + if (err.name === "YAMLException") { + console.error(`\x1b[31m[ERROR]\x1b[0m Failed to read ${ui5YamlPath}!`); + process.exit(1); + } + throw err; +} diff --git a/packages/ui5-task-copyright/lib/copyright.js b/packages/ui5-task-copyright/lib/copyright.js new file mode 100644 index 000000000..3b1c04fc9 --- /dev/null +++ b/packages/ui5-task-copyright/lib/copyright.js @@ -0,0 +1,147 @@ +const fs = require("fs"); + +const { parse } = require("@typescript-eslint/typescript-estree"); +const { XMLParser } = require("fast-xml-parser"); + +/** + * Task to append a copyright header for TypeScript, JavaScript and XML files + * @param {object} parameters Parameters + * @param {module:@ui5/logger/Logger} parameters.log Logger instance + * @param {module:@ui5/fs.DuplexCollection} parameters.workspace DuplexCollection to read and write files + * @param {object} parameters.options Options + * @returns {Promise} Promise resolving with undefined once data has been written + */ +module.exports = async ({ log, workspace, options }) => { + // disable task if no copyright is configured + if (!options?.configuration?.copyright) { + return Promise.resolve(); + } + + // determine the copyright and current year placeholders + const { copyrightPlaceholder, currentYearPlaceholder } = Object.assign( + { + copyrightPlaceholder: "copyright", + currentYearPlaceholder: "currentYear", + }, + options.configuration + ); + + // create regular expressions for the placeholders + const copyrightRegExp = new RegExp(`(?:\\$\\{${copyrightPlaceholder}\\}|@${copyrightPlaceholder}@)`, "g"); + const currentYearRegExp = new RegExp(`(?:\\$\\{${currentYearPlaceholder}\\}|@${currentYearPlaceholder}@)`, "g"); + + // determine the actual copyright or default it + let { copyright, excludePatterns } = Object.assign( + { + copyright: `\${${copyrightPlaceholder}}`, + }, + options.configuration + ); + + // check if the file exists (if copyright is a file path) + if (fs.existsSync(copyright)) { + log.verbose(`Reading copyright from file ${copyright}...`); + copyright = fs.readFileSync(copyright, "utf-8").trim(); + } + + // Replace optional placeholder ${currentYear} with the current year + const copyrightString = copyright.replace(currentYearRegExp, new Date().getFullYear()); + + // process the script resources + const scriptResources = await workspace.byGlob(`**/*.+(ts|js)`); + if (scriptResources.length > 0) { + await Promise.all( + scriptResources.map(async (resource) => { + const resourcePath = resource.getPath(); + + // check if the resource should be excluded + if (excludePatterns && excludePatterns.some((pattern) => resourcePath.includes(pattern))) { + return; + } + + // detailed logging + log.verbose("Processing file " + resourcePath); + + // read the resource and parse the code + const code = await resource.getString(); + const ast = parse(code, { + filePath: resourcePath, + loc: true, + range: true, + comment: true, + }); + + // find the first comment that starts with "!" and has a copyright placeholder + const copyrightComment = ast.comments.find((comment) => { + return comment.type === "Block" && comment.value.startsWith("!") && copyrightRegExp.test(comment.value); + }); + + // preprend the comment or replace the copyight placeholder + const copyrightForJS = copyrightString + .split(/\r?\n/) + .map((line) => line.trimEnd()) + .join("\n * "); + if (!copyrightComment) { + await resource.setString(`/*!\n * ${copyrightForJS}\n */\n${code}`); + } else { + await resource.setString(code.replace(copyrightRegExp, copyrightForJS)); + } + + // write the resource + await workspace.write(resource); + }) + ); + } + + // process the xml resources + const xmlResources = await workspace.byGlob(`**/*.xml`); + if (xmlResources.length > 0) { + // use fast-xml-parser to parse the xml files (including comments) + const parser = new XMLParser({ + commentPropName: "#comment", + }); + await Promise.all( + xmlResources.map(async (resource) => { + const resourcePath = resource.getPath(); + + // check if the resource should be excluded + if (excludePatterns && excludePatterns.some((pattern) => resourcePath.includes(pattern))) { + return; + } + + // detailed logging + log.verbose("Processing file " + resourcePath); + + // read the resource and parse the xml + const code = await resource.getString(); + const xml = parser.parse(code); + + // preprend the comment or replace the copyight placeholder + const copyrightForXML = copyrightString + .split(/\r?\n/) + .map((line) => line.trimEnd()) + .join("\n "); + if (!(xml["#comment"] && copyrightRegExp.test(xml["#comment"]))) { + await resource.setString(`\n${code}`); + } else { + await resource.setString(code.replace(copyrightRegExp, copyrightForXML)); + } + + // write the resource + await workspace.write(resource); + }) + ); + } +}; + +/** + * Callback function to define the list of required dependencies + * @returns {Promise} + * Promise resolving with a Set containing all dependencies + * that should be made available to the task. + * UI5 Tooling will ensure that those dependencies have been + * built before executing the task. + */ +module.exports.determineRequiredDependencies = async function () { + return new Set(); +}; diff --git a/packages/ui5-task-copyright/package.json b/packages/ui5-task-copyright/package.json new file mode 100644 index 000000000..275efc221 --- /dev/null +++ b/packages/ui5-task-copyright/package.json @@ -0,0 +1,38 @@ +{ + "name": "ui5-task-copyright", + "version": "2.0.0", + "description": "UI5 task for appending copyright headers for TypeScript, JavaScript and XML files", + "author": "Peter Muessig", + "license": "Apache-2.0", + "homepage": "https://github.com/ui5-community/ui5-ecosystem-showcase/tree/main/packages/ui5-task-copyright#readme", + "repository": { + "type": "git", + "url": "https://github.com/ui5-community/ui5-ecosystem-showcase.git", + "directory": "packages/ui5-task-copyright" + }, + "bin": { + "uses-ui5-task-copyright": "./bin/uses-ui5-task-copyright.js" + }, + "scripts": { + "lint": "eslint lib", + "test": "ava" + }, + "ava": { + "files": [ + "test/**/*", + "!test/__assets__/**/*", + "!test/__dist__/**/*" + ], + "verbose": true, + "timeout": "5m" + }, + "devDependencies": { + "@ui5/cli": "^3.8.0", + "ava": "^5.3.1" + }, + "dependencies": { + "@typescript-eslint/typescript-estree": "^7.0.1", + "fast-xml-parser": "^4.3.3", + "js-yaml": "^4.1.0" + } +} diff --git a/packages/ui5-task-copyright/test/__assets__/ui5.file.yaml b/packages/ui5-task-copyright/test/__assets__/ui5.file.yaml new file mode 100644 index 000000000..6a6070da4 --- /dev/null +++ b/packages/ui5-task-copyright/test/__assets__/ui5.file.yaml @@ -0,0 +1,24 @@ +specVersion: "3.0" +metadata: + name: ui5.ecosystem.demo.tsapp +type: application +framework: + name: OpenUI5 + version: "1.120.1" + libraries: + - name: sap.m + - name: sap.ui.core + - name: sap.ui.unified + - name: themelib_sap_horizon +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion + configuration: + generateDts: true + generateTsInterfaces: true + omitTSFromBuildResult: false + - name: ui5-task-copyright + beforeTask: replaceCopyright + configuration: + copyright: ./copyright.txt diff --git a/packages/ui5-task-copyright/test/__assets__/ui5.inline.yaml b/packages/ui5-task-copyright/test/__assets__/ui5.inline.yaml new file mode 100644 index 000000000..9bf037dbe --- /dev/null +++ b/packages/ui5-task-copyright/test/__assets__/ui5.inline.yaml @@ -0,0 +1,25 @@ +specVersion: "3.0" +metadata: + name: ui5.ecosystem.demo.tsapp +type: application +framework: + name: OpenUI5 + version: "1.120.1" + libraries: + - name: sap.m + - name: sap.ui.core + - name: sap.ui.unified + - name: themelib_sap_horizon +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion + configuration: + generateDts: true + generateTsInterfaces: true + omitTSFromBuildResult: false + - name: ui5-task-copyright + beforeTask: replaceCopyright + configuration: + copyright: "Copyright ${currentYear} UI5 Community + \nAll rights reserved." diff --git a/packages/ui5-task-copyright/test/__assets__/ui5.no.copyright.yaml b/packages/ui5-task-copyright/test/__assets__/ui5.no.copyright.yaml new file mode 100644 index 000000000..04edb7258 --- /dev/null +++ b/packages/ui5-task-copyright/test/__assets__/ui5.no.copyright.yaml @@ -0,0 +1,22 @@ +specVersion: "3.0" +metadata: + name: ui5.ecosystem.demo.tsapp +type: application +framework: + name: OpenUI5 + version: "1.120.1" + libraries: + - name: sap.m + - name: sap.ui.core + - name: sap.ui.unified + - name: themelib_sap_horizon +builder: + customTasks: + - name: ui5-tooling-transpile-task + afterTask: replaceVersion + configuration: + generateDts: true + generateTsInterfaces: true + omitTSFromBuildResult: false + - name: ui5-task-copyright + beforeTask: replaceCopyright diff --git a/packages/ui5-task-copyright/test/__dist__/.keepMe b/packages/ui5-task-copyright/test/__dist__/.keepMe new file mode 100644 index 000000000..e69de29bb diff --git a/packages/ui5-task-copyright/test/copyright.test.js b/packages/ui5-task-copyright/test/copyright.test.js new file mode 100644 index 000000000..3f6cdb395 --- /dev/null +++ b/packages/ui5-task-copyright/test/copyright.test.js @@ -0,0 +1,96 @@ +const path = require("path"); +const { mkdirSync, rmSync, readFileSync } = require("fs"); +const { spawnSync } = require("child_process"); +const crypto = require("crypto"); + +const test = require("ava"); + +test.beforeEach(async (t) => { + // prepare + t.context.tmpDir = path.resolve(`./test/__dist__/${crypto.randomBytes(5).toString("hex")}`); + mkdirSync(t.context.tmpDir); +}); + +test.afterEach.always(async (t) => { + // cleanup + rmSync(t.context.tmpDir, { recursive: true, force: true }); +}); + +// eslint-disable-next-line jsdoc/require-jsdoc +function startsWithCopyright(file, copyrightString) { + const content = readFileSync(file, "utf-8"); + if (file.endsWith(".js") || file.endsWith(".ts")) { + const copyrightForJS = copyrightString + .split(/\r?\n/) + .map((line) => line.trimEnd()) + .join("\n * "); + return content.startsWith(`/*!\n * ${copyrightForJS}\n */\n`); + } else if (file.endsWith(".xml")) { + const copyrightForXML = copyrightString + .split(/\r?\n/) + .map((line) => line.trimEnd()) + .join("\n "); + return content.startsWith(`\n`); + } + return false; +} + +test("Inline copyright", async (t) => { + const ui5 = { yaml: path.resolve("./test/__assets__/ui5.inline.yaml") }; + spawnSync(`ui5 build --config ${ui5.yaml} --dest ${t.context.tmpDir}/dist`, { + stdio: "inherit", // > don't include stdout in test output, + shell: true, + cwd: path.resolve(__dirname, "../../../showcases/ui5-tsapp"), + }); + + // check files to include copyright + const copyright = `Copyright ${new Date().getFullYear()} UI5 Community\nAll rights reserved.`; + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "Component.js"), copyright)); + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "Component-dbg.js"), copyright)); + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "controller/App.controller.js"), copyright)); + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "controller/App-dbg.controller.js"), copyright)); + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "controller/Main.controller.js"), copyright)); + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "controller/Main-dbg.controller.js"), copyright)); + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "view/App.view.xml"), copyright)); + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "view/Main.view.xml"), copyright)); +}); + +test("Copyright from file", async (t) => { + const ui5 = { yaml: path.resolve("./test/__assets__/ui5.file.yaml") }; + spawnSync(`ui5 build --config ${ui5.yaml} --dest ${t.context.tmpDir}/dist`, { + stdio: "inherit", // > don't include stdout in test output, + shell: true, + cwd: path.resolve(__dirname, "../../../showcases/ui5-tsapp"), + }); + + // check files to include copyright + const copyright = `Copyright ${new Date().getFullYear()} UI5 Community\nAll rights reserved.`; + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "Component.js"), copyright)); + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "Component-dbg.js"), copyright)); + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "controller/App.controller.js"), copyright)); + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "controller/App-dbg.controller.js"), copyright)); + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "controller/Main.controller.js"), copyright)); + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "controller/Main-dbg.controller.js"), copyright)); + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "view/App.view.xml"), copyright)); + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "view/Main.view.xml"), copyright)); +}); + +test("No copyright", async (t) => { + const ui5 = { yaml: path.resolve("./test/__assets__/ui5.no.copyright.yaml") }; + spawnSync(`ui5 build --config ${ui5.yaml} --dest ${t.context.tmpDir}/dist`, { + stdio: "inherit", // > don't include stdout in test output, + shell: true, + cwd: path.resolve(__dirname, "../../../showcases/ui5-tsapp"), + }); + + // check files to include copyright + const copyright = `\${copyright}`; + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "Component.js"), copyright)); + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "Component-dbg.js"), copyright)); + t.false(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "controller/App.controller.js"), copyright)); + t.false(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "controller/App-dbg.controller.js"), copyright)); + t.false(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "controller/Main.controller.js"), copyright)); + t.false(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "controller/Main-dbg.controller.js"), copyright)); + t.true(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "view/App.view.xml"), copyright)); + t.false(startsWithCopyright(path.resolve(t.context.tmpDir, "dist", "view/Main.view.xml"), copyright)); +}); diff --git a/packages/ui5-task-copyright/ui5.yaml b/packages/ui5-task-copyright/ui5.yaml new file mode 100644 index 000000000..8f6578090 --- /dev/null +++ b/packages/ui5-task-copyright/ui5.yaml @@ -0,0 +1,8 @@ +# https://sap.github.io/ui5-tooling/pages/extensibility/CustomTasks/ +specVersion: "3.0" +metadata: + name: ui5-task-copyright +kind: extension +type: task +task: + path: lib/copyright.js diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 53767bb67..e6d897cd9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -383,6 +383,25 @@ importers: specifier: ^5.3.1 version: 5.3.1 + packages/ui5-task-copyright: + dependencies: + '@typescript-eslint/typescript-estree': + specifier: ^7.0.1 + version: 7.0.1(typescript@5.3.3) + fast-xml-parser: + specifier: ^4.3.3 + version: 4.3.3 + js-yaml: + specifier: ^4.1.0 + version: 4.1.0 + devDependencies: + '@ui5/cli': + specifier: ^3.8.0 + version: 3.8.0 + ava: + specifier: ^5.3.1 + version: 5.3.1 + packages/ui5-task-flatten-library: {} packages/ui5-task-i18ncheck: @@ -942,6 +961,9 @@ importers: ui5-middleware-livereload: specifier: workspace:^ version: link:../../packages/ui5-middleware-livereload + ui5-task-copyright: + specifier: workspace:^ + version: link:../../packages/ui5-task-copyright ui5-tooling-modules: specifier: workspace:^ version: link:../../packages/ui5-tooling-modules @@ -4674,6 +4696,11 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dev: true + /@typescript-eslint/types@7.0.1: + resolution: {integrity: sha512-uJDfmirz4FHib6ENju/7cz9SdMSkeVvJDK3VcMFvf/hAShg8C74FW+06MaQPODHfDJp/z/zHfgawIJRjlu0RLg==} + engines: {node: ^16.0.0 || >=18.0.0} + dev: false + /@typescript-eslint/typescript-estree@6.16.0(typescript@5.3.3): resolution: {integrity: sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==} engines: {node: ^16.0.0 || >=18.0.0} @@ -4696,6 +4723,28 @@ packages: - supports-color dev: true + /@typescript-eslint/typescript-estree@7.0.1(typescript@5.3.3): + resolution: {integrity: sha512-SO9wHb6ph0/FN5OJxH4MiPscGah5wjOd0RRpaLvuBv9g8565Fgu0uMySFEPqwPHiQU90yzJ2FjRYKGrAhS1xig==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 7.0.1 + '@typescript-eslint/visitor-keys': 7.0.1 + debug: 4.3.4(supports-color@8.1.1) + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.5.4 + ts-api-utils: 1.0.3(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: false + /@typescript-eslint/utils@6.16.0(eslint@8.56.0)(typescript@5.3.3): resolution: {integrity: sha512-T83QPKrBm6n//q9mv7oiSvy/Xq/7Hyw9SzSEhMHJwznEmQayfBM87+oAlkNAMEO7/MjIwKyOHgBJbxB0s7gx2A==} engines: {node: ^16.0.0 || >=18.0.0} @@ -4723,6 +4772,14 @@ packages: eslint-visitor-keys: 3.4.3 dev: true + /@typescript-eslint/visitor-keys@7.0.1: + resolution: {integrity: sha512-hwAgrOyk++RTXrP4KzCg7zB2U0xt7RUU0ZdMSCsqF3eKUwkdXUMyTb0qdCuji7VIbcpG62kKTU9M1J1c9UpFBw==} + engines: {node: ^16.0.0 || >=18.0.0} + dependencies: + '@typescript-eslint/types': 7.0.1 + eslint-visitor-keys: 3.4.3 + dev: false + /@ui5/builder@3.2.0: resolution: {integrity: sha512-n6Z34iandeGPUS2x0OM7AdEvmnLbqqk9mfGuismuWx8gov2cawuJ/42B0BLamXRmlTqFPB7eGnaWry9tDK6h3Q==} engines: {node: ^16.18.0 || >=18.12.0, npm: '>= 8'} @@ -5502,7 +5559,6 @@ packages: /array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - dev: true /arraybuffer.prototype.slice@1.0.2: resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==} @@ -9496,7 +9552,6 @@ packages: ignore: 5.2.4 merge2: 1.4.1 slash: 3.0.0 - dev: true /globby@13.2.2: resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} @@ -14712,7 +14767,6 @@ packages: /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - dev: true /slash@4.0.0: resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} @@ -15594,7 +15648,6 @@ packages: typescript: '>=4.2.0' dependencies: typescript: 5.3.3 - dev: true /tsconfig-paths@4.2.0: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} diff --git a/showcases/ui5-tsapp/copyright.txt b/showcases/ui5-tsapp/copyright.txt new file mode 100644 index 000000000..33285e3b3 --- /dev/null +++ b/showcases/ui5-tsapp/copyright.txt @@ -0,0 +1,2 @@ +Copyright ${currentYear} UI5 Community +All rights reserved. diff --git a/showcases/ui5-tsapp/package.json b/showcases/ui5-tsapp/package.json index 8a609d2d2..77619e02d 100644 --- a/showcases/ui5-tsapp/package.json +++ b/showcases/ui5-tsapp/package.json @@ -49,6 +49,7 @@ "rimraf": "^5.0.5", "typescript": "^5.3.3", "ui5-middleware-livereload": "workspace:^", + "ui5-task-copyright": "workspace:^", "ui5-tooling-modules": "workspace:^", "ui5-tooling-transpile": "workspace:^" } diff --git a/showcases/ui5-tsapp/ui5.yaml b/showcases/ui5-tsapp/ui5.yaml index b12a808fa..d02202bd2 100644 --- a/showcases/ui5-tsapp/ui5.yaml +++ b/showcases/ui5-tsapp/ui5.yaml @@ -41,6 +41,12 @@ builder: addToNamespace: true providedDependencies: - moment + - name: ui5-task-copyright + beforeTask: replaceCopyright + configuration: + copyright: ./copyright.txt + excludePatterns: + - "Component.ts" server: customMiddleware: - name: ui5-tooling-transpile-middleware diff --git a/showcases/ui5-tsapp/webapp/Component.ts b/showcases/ui5-tsapp/webapp/Component.ts index 5532edc24..7ce84615c 100644 --- a/showcases/ui5-tsapp/webapp/Component.ts +++ b/showcases/ui5-tsapp/webapp/Component.ts @@ -1,3 +1,6 @@ +/*! + * ${copyright} + */ import UIComponent from "sap/ui/core/UIComponent"; import Device from "sap/ui/Device"; diff --git a/showcases/ui5-tsapp/webapp/view/App.view.xml b/showcases/ui5-tsapp/webapp/view/App.view.xml index 3da5e7009..0f1aca456 100644 --- a/showcases/ui5-tsapp/webapp/view/App.view.xml +++ b/showcases/ui5-tsapp/webapp/view/App.view.xml @@ -1,3 +1,6 @@ +