diff --git a/application/views/Studentenverwaltung.php b/application/views/Studentenverwaltung.php index 5b6138ff8..cb86feb54 100644 --- a/application/views/Studentenverwaltung.php +++ b/application/views/Studentenverwaltung.php @@ -30,7 +30,7 @@ #'vendor/npm-asset/primevue/toast/toast.min.js' ], 'customJSModules' => [ - 'public/js/apps/Studentenverwaltung.js' + 'public/dist/js/apps/Studentenverwaltung.js' ] ); diff --git a/package.json b/package.json index 66b7246c1..dde4ebdfd 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,8 @@ { + "name": "fhc-core", + "private": true, + "version": "0.0.0", + "type": "module", "scripts": { "cy:open": "cypress open", "cy:run": "cypress run", @@ -9,12 +13,35 @@ "cy:noten:punkte-on": "cypress run --browser chrome --env NOTEN_PUNKTE_MODUS=on --spec \"tests/cypress/e2e/specs/**/*.cy.js\"", "cy:noten:punkte-off": "cypress run --browser chrome --env NOTEN_PUNKTE_MODUS=off --spec \"tests/cypress/e2e/specs/**/*.cy.js\"", "cy:noten:p0": "cypress run --spec \"tests/cypress/e2e/specs/unit/notenRules.cy.js,tests/cypress/e2e/specs/api/noten/noten.pruefungsantritte.cy.js,tests/cypress/e2e/specs/api/noten/noten.pruefungstermin.cy.js,tests/cypress/e2e/specs/api/noten/noten.ueberschreiben.cy.js\"", - "noten:check": "node tests/cypress/tools/dbCheck.js noten" + "noten:check": "node tests/cypress/tools/dbCheck.js noten", + "build": "rollup -c", + "watch": "rollup --watch -c" }, "dependencies": { "cypress": "^15.11.0", "dotenv": "^17.4.2", "pg": "^8.13.1", - "ssh2": "^1.17.0" + "ssh2": "^1.17.0", + "@vuepic/vue-datepicker": "^7.2.0", + "axios": "^1.6.8", + "primevue": "^3.29.1", + "tabulator-tables": "^5.5.2", + "vue": "^3.3.8", + "vue-router": "^4.1.3" + }, + "devDependencies": { + "@rollup/plugin-alias": "^5.1.0", + "@rollup/plugin-babel": "^6.0.4", + "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-replace": "^5.0.5", + "@rollup/plugin-terser": "^0.4.4", + "babel-plugin-transform-class-properties": "^6.24.1", + "glob": "^11.0.3", + "node-sass": "^9.0.0", + "rollup": "^4.52.4", + "rollup-plugin-postcss": "^4.0.2", + "rollup-plugin-vue": "^6.0.0" } } diff --git a/rollup.README.md b/rollup.README.md new file mode 100644 index 000000000..6e4c8a446 --- /dev/null +++ b/rollup.README.md @@ -0,0 +1,25 @@ +***NVM installieren*** + +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash + +oder + +wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash + +***Node.js installieren*** + +nvm install --lts + +***package.json*** + +npm install + +***Rollup build*** + +einmalig: + +npm run build + +als watch bei Änderungen: + +npm run watch diff --git a/rollup.chunks.config.js b/rollup.chunks.config.js new file mode 100644 index 000000000..58254821f --- /dev/null +++ b/rollup.chunks.config.js @@ -0,0 +1,99 @@ +import babel from '@rollup/plugin-babel'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import vue from "rollup-plugin-vue"; +import { globSync } from 'glob'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import postcss from 'rollup-plugin-postcss'; +import replace from '@rollup/plugin-replace'; +import alias from '@rollup/plugin-alias'; +import { existsSync } from 'node:fs'; +import terser from '@rollup/plugin-terser'; +import json from '@rollup/plugin-json'; + +function FhcResolver () { + return { + name: 'fhc-resolver', // this name will show up in logs and errors + resolveId ( source, importer, options ) { + if( source.includes('.php') ) { + return false; + } + //console.log('source: ' + source + ' options.isEntry: ' + options.isEntry + ' importer: ' + importer); + if(importer !== undefined && !options.isEntry && !path.isAbsolute(source)) { + let tmp = path.dirname(importer); + if( importer.includes('/application/') ) { + tmp = tmp.replace(/public\/js\//, 'js/').replace(/\/application\//, '/public/'); + } + const resolved = path.resolve(tmp, source); + //console.log(resolved); + if( existsSync(resolved) ) { + return resolved + } + } + return null; // other ids should be handled as usually + } + }; +} + +export default { + input: Object.fromEntries( + globSync('public/**/js/apps/**/*.js', {follow: true, realpath: true}).map(file => { + if( path.dirname(file).includes('/dist/') || path.dirname(file).includes('/apps/vbform') ) { + return null; + } + // This remove `src/` as well as the file extension from each + // file, so e.g. src/nested/foo.js becomes nested/foo + return [path.relative( + '', + file.slice(0, file.length - path.extname(file).length) + ).replace(/public\//, 'public/dist/'), + // This expands the relative paths to absolute paths, so e.g. + // src/nested/foo becomes /project/src/nested/foo.js + fileURLToPath(new URL(file, import.meta.url))] + }).filter(Boolean) + ), + plugins: [ + alias({ + entries: { + vue: 'vue/dist/vue.esm-bundler.js' + } + }), + nodeResolve({ + preferBuiltins: true, + moduleDirectories: ['node_modules'], + modulePaths: globSync('application/extensions/*/node_modules', {follow: true, realpath: true}).map(file => + fileURLToPath(new URL(file, import.meta.url)) + ), + }), + FhcResolver(), + replace({ + preventAssignment: true, + 'process.env.NODE_ENV': JSON.stringify( 'production' ), + }), + commonjs(), + vue(), + json(), + babel({ + babelHelpers: 'bundled', + plugins: ['transform-class-properties'] + }), + postcss({ + extract: false, + modules: true, + use: ['sass'], + }), + terser() + ], + watch: { + buildDelay: 500 + }, + output: { + preserveModules: false, + sourcemap: true, + format: 'es', + dir: './', + //manualChunks: {} + chunkFileNames: 'public/dist/js/includes/[name]-[hash].js' + } +}; diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 000000000..1bcc1d014 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,120 @@ +import babel from '@rollup/plugin-babel'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import vue from "rollup-plugin-vue"; +import { globSync } from 'glob'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import postcss from 'rollup-plugin-postcss'; +import replace from '@rollup/plugin-replace'; +import alias from '@rollup/plugin-alias'; +import { existsSync } from 'node:fs'; +import terser from '@rollup/plugin-terser'; +import json from '@rollup/plugin-json'; + +function FhcResolver () { + return { + name: 'fhc-resolver', // this name will show up in logs and errors + resolveId ( source, importer, options ) { + if( source.includes('vueDatepicker.js.php') ) { + return {id: 'vueDatepicker.js.php', external: 'relative'}; + } + if( source.includes('index.ci.php') ) { + return { id: '../' + source, external: 'relative'}; + } + if( source.includes('.php') ) { + return false; + } + //console.log('source: ' + source + ' options.isEntry: ' + options.isEntry + ' importer: ' + importer); + if(importer !== undefined && !options.isEntry && !path.isAbsolute(source)) { + let tmp = path.dirname(importer); + if( importer.includes('/application/') ) { + tmp = tmp.replace(/public\/js\//, 'js/').replace(/\/application\//, '/public/'); + } + const resolved = path.resolve(tmp, source); + //console.log(resolved); + if( existsSync(resolved) ) { + return resolved + } + } + return null; // other ids should be handled as usually + } + }; +} + +const useplugins = [ + alias({ + entries: { + vue: 'vue/dist/vue.esm-bundler.js', + } + }), + commonjs(), + nodeResolve({ + module: true, + jsnext: true, + preferBuiltins: true, + browser: true, + moduleDirectories: ['node_modules'], + modulePaths: globSync('application/extensions/*/node_modules', {follow: true, realpath: true}).map(file => + fileURLToPath(new URL(file, import.meta.url)) + ), + }), + json({ + compact: true + }), + FhcResolver(), + replace({ + preventAssignment: true, + 'process.env.NODE_ENV': JSON.stringify( 'production' ), + }), + vue(), + babel({ + babelHelpers: 'bundled', + plugins: ['transform-class-properties'], + }), +/* + postcss({ + extract: false, + modules: true, + use: ['sass'], + }), +*/ + terser() +]; + +export default globSync('public/**/js/apps/**/*.js', {follow: true, realpath: true}).map(file => { + if( path.dirname(file).includes('/dist/') || path.dirname(file).includes('/apps/vbform') ) { + return null; + } + const tmp = fileURLToPath(new URL(file, import.meta.url)); + const basepath = tmp.replace(/\/public\/js\/.*/, '/public/js/'); + const outputfile = tmp.replace(/public\//, 'public/dist/'); + let cssfile = path.basename(tmp.replace(/\.js/, '.css')); + console.log('cssfile: ' + cssfile); + let cssplugin = [ + postcss({ + extract: cssfile, + minimize: true, + sourceMap: true + }) + ]; + const vuedatepicker = path.relative(path.dirname(outputfile), basepath + 'components/vueDatepicker.js.php'); + console.log(vuedatepicker); + return { + input: tmp, + plugins: [...useplugins, ...cssplugin], + watch: { + buildDelay: 500 + }, + output: { + preserveModules: false, + inlineDynamicImports: true, + sourcemap: true, + format: 'es', + file: outputfile, + paths: { + "vueDatepicker.js.php": vuedatepicker + } + } + } + }).filter(Boolean);