diff --git a/README.md b/README.md index 6897554..6d33fc1 100644 --- a/README.md +++ b/README.md @@ -137,9 +137,14 @@ To build everything and produce the installable template: ## Changelog +### 3.4.0 + +- Update to Tailwind CSS v1.4 +- Reorganization of the build scripts + ### 3.3.5 -Update to Tailwind CSS v1.3.5. +- Update to Tailwind CSS v1.3.5 ### 3.3.0 diff --git a/build/build_core.sh b/build/build_core.sh index 5e819ac..f75d899 100755 --- a/build/build_core.sh +++ b/build/build_core.sh @@ -1,17 +1,13 @@ #!/bin/sh -cd template/ -rm css/template*.css -rm dist/main*.css +cd library +/usr/bin/composer update --no-dev +/usr/bin/composer dump-autoload --classmap-authoritative +cd .. +cd template npm ci npm run prod cd .. -rm -rf template/node_modules - -# JavaScript to be deferred -cat template/dist/main.js > template/js/template.js -cat template/js/prism.js >> template/js/template.js - npm run build diff --git a/webpack.config.js b/webpack.config.js index f5c67a0..04cd215 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -7,7 +7,7 @@ * */ -// Array of Generation plugins +// Array of Webpack plugins let buildPlugins = []; // Extension directories to be visited @@ -22,86 +22,243 @@ const extensionTypesDirs = [ 'platform', ]; -// Required plugins -const path = require('path'); -const ZipFilesPlugin = require('webpack-zip-files-plugin'); +// Only template xml are processed, +// other xml config files are ignored +const packageIgnoreFiles = ['config.xml']; + +// WARNING - Clean these development folders before building +// This is highly opinionated. +// NEVER USE A FOLDER WITH THESE NAMES IN THE FINAL PACKAGE. +const globalCleanDevAssets = [ + /* + // Directories to be purged + /\/\.git\//, + /\/\.github\//, + /\/\.phan\//, + /\/\.vscode\//, + /\/bin\//, + /\/build\//, + /\/demo\//, + /\/doc\//, + /\/docs\//, + /\/Documentation\//, + /\/examples\//, + /\/ext\//, + /\/nbproject\//, + */ + /\/node_modules\//, + /* + /\/style\//, + + // Files to be purged + // Files ending with these extesion types + /\.neon$/, + /\.sh$/, + /\.twig$/, + /\.xlf$/, + + /\.coveralls\.yml$/, + /\.editorconfig$/, + /\.gitattributes$/, + /\.gitignore$/, + /\.hhconfig$/, + /\.Mime$/, + /\.php_cs\.dist$/, + /\.php_cs$/, + /\.scrutinizer\.yml$/, + /\.State$/, + /\.styleci\.yml$/, + /\.travis\.yml$/, + /AUTHORS$/, + /behat\.yml$/, + /build\.php$/, + /build\.properties$/, + /build\.xml$/, + /CHANGELOG\.md$/, + /CHANGELOG\.mdown$/, + /CHANGELOG$/, + /CHANGES$/, + /circle\.yml$/, + /CODE_OF_CONDUCT\.md$/, + /CONDUCT\.md$/, + /CONTRIBUTING\.md$/, + /COPYING$/, + /db\.sql$/, + /docker-compose\.yml$/, + /example\.php$/, + /FastRoute\.hhi$/, + /gulp-config\.ci\.json$/, + /Makefile$/, + /mkdocs\.yml$/, + /package\.xml$/, + /phpcs\.xml\.dist$/, + /phpcs\.xml$/, + /phpmd\.xml$/, + /phpunit\.xml\.dist$/, + /phpunit*\.xml$/, + /psalm\.xml$/, + /puli\.json$/, + /README\.markdown$/, + /readme\.md$/, + /Readme\.md$/, + /README\.md$/, + /README\.rst$/, + /ReadMe\.txt$/, + /sonar-project\.properties$/, + /UPGRADE_TO_2_1$/, + /UPGRADE_TO_2_2$/, + /Upgrade\.md$/, + /UPGRADE\.md$/, + /UPGRADE$/, + /UPGRADING\.md$/, + /VERSION$/, + */ +]; + +// Required Webpack plugins const CopyWebpackPlugin = require('copy-webpack-plugin'); -const readDirRecursive = require('fs-readdir-recursive'); +const Dotenv = require('dotenv-webpack'); +const FileManagerPlugin = require('filemanager-webpack-plugin'); const fs = require('fs'); const fsExtra = require('fs-extra'); -const Dotenv = require('dotenv-webpack'); const moment = require('moment'); +const path = require('path'); +const readDirRecursive = require('fs-readdir-recursive'); +const ZipFilesPlugin = require('webpack-zip-files-plugin'); +const glob = require("glob"); let definitions; -const releaseDate = moment().format('YYYY-MM-DD'); -const year = moment().format('YYYY'); +const releaseDate = moment() + .format('YYYY-MM-DD'); +const year = moment() + .format('YYYY'); const releaseDir = 'build/release'; const releaseDirAbs = path.resolve(__dirname, releaseDir); const templatesDir = 'build/templates'; const translationsDir = 'build/translations'; const packageDirAbs = path.resolve(__dirname, packageTypeDir); +const renderDirectories = [templatesDir, translationsDir]; +const allExtensionTypesDirs = extensionTypesDirs.concat([packageTypeDir]); + +const tagTransformation = (content) => content + .toString() + .replace(/\[MANIFEST_COPYRIGHT\]/g, definitions.MANIFEST_COPYRIGHT) + .replace(/; \[TRANSLATION_COPYRIGHT\]/g, definitions.TRANSLATION_COPYRIGHT) + .replace('// [PHP_COPYRIGHT]', definitions.PHP_COPYRIGHT) + .replace('/* [CSS_COPYRIGHT] */', definitions.CSS_COPYRIGHT) + .replace('// [JS_COPYRIGHT]', definitions.JS_COPYRIGHT) + .replace(/\[COPYRIGHT\]/g, definitions.COPYRIGHT) + .replace(/\[AUTHOR_EMAIL\]/g, definitions.AUTHOR_EMAIL) + .replace(/\[AUTHOR_URL\]/g, definitions.AUTHOR_URL) + .replace(/\[AUTHOR\]/g, definitions.AUTHOR) + .replace(/\[EXTENSION_CDN\]/g, definitions.EXTENSION_CDN) + .replace(/\[EXTENSION_CLASS_NAME\]/g, definitions.EXTENSION_CLASS_NAME) + .replace(/\[EXTENSION_ALIAS\]/g, definitions.EXTENSION_ALIAS) + .replace(/\[EXTENSION_DESC\]/g, definitions.EXTENSION_DESC) + .replace(/\[EXTENSION_NAME\]/g, definitions.EXTENSION_NAME) + .replace(/\[LICENSE_CODE\]/g, definitions.LICENSE_CODE) + .replace(/\[LICENSE\]/g, definitions.LICENSE) + .replace(/\[RELEASE_VERSION\]/g, definitions.RELEASE_VERSION) + .replace(/\[TRANSLATION_KEY\]/g, definitions.TRANSLATION_KEY) + .replace(/\[DATE\]/g, releaseDate) + .replace(/\[YEAR\]/g, year); function loadEnvironmentDefinitions() { const defs = {}; const env = new Dotenv(); - Object.keys(env.definitions).forEach((definition) => { - const key = definition.replace('process.env.', ''); - let value = env.definitions[definition]; + Object.keys(env.definitions) + .forEach((definition) => { + const key = definition.replace('process.env.', ''); + let value = env.definitions[definition]; - value = value.replace(/^"(.+(?="$))"$/, '$1'); - value = value.replace(/%CR%/g, '\n'); - value = value.replace(/%TAB%/g, '\t'); + value = value.replace(/^"(.+(?="$))"$/, '$1'); + value = value.replace(/%CR%/g, '\n'); + value = value.replace(/%TAB%/g, '\t'); - defs[key] = value; - }); + defs[key] = value; + }); return defs; } +function cleanDevAssets() { + const cleanDevAssetsDirs = allExtensionTypesDirs.map( + // Read all files + (extensionTypesDir) => glob.sync( + path.resolve(__dirname, extensionTypesDir) + '/**/*', { + dot: true + } + ) + ) + // One flat array + .flat() + // Filter to files that match the globalCleanDevAssets to clean + .filter((item) => { + return globalCleanDevAssets.find((globalCleanDevFolder) => { + return globalCleanDevFolder.test(item); + }); + }); + + cleanDevAssetsDirs.map((file) => fsExtra.removeSync(file)); +} + function removeReleaseDirectory() { - fsExtra.removeSync(releaseDirAbs); - fs.mkdirSync(releaseDirAbs); + return new FileManagerPlugin({ + onStart: { + delete: [ + releaseDirAbs, + ], + mkdir: [ + releaseDirAbs, + ], + } + }); } -const tagTransformation = (content) => content - .toString() - .replace(/\[MANIFEST_COPYRIGHT\]/g, definitions.MANIFEST_COPYRIGHT) - .replace(/; \[TRANSLATION_COPYRIGHT\]/g, definitions.TRANSLATION_COPYRIGHT) - .replace('// [PHP_COPYRIGHT]', definitions.PHP_COPYRIGHT) - .replace('/* [CSS_COPYRIGHT] */', definitions.CSS_COPYRIGHT) - .replace('// [JS_COPYRIGHT]', definitions.JS_COPYRIGHT) - .replace(/\[COPYRIGHT\]/g, definitions.COPYRIGHT) - .replace(/\[AUTHOR_EMAIL\]/g, definitions.AUTHOR_EMAIL) - .replace(/\[AUTHOR_URL\]/g, definitions.AUTHOR_URL) - .replace(/\[AUTHOR\]/g, definitions.AUTHOR) - .replace(/\[EXTENSION_CDN\]/g, definitions.EXTENSION_CDN) - .replace(/\[EXTENSION_CLASS_NAME\]/g, definitions.EXTENSION_CLASS_NAME) - .replace(/\[EXTENSION_ALIAS\]/g, definitions.EXTENSION_ALIAS) - .replace(/\[EXTENSION_DESC\]/g, definitions.EXTENSION_DESC) - .replace(/\[EXTENSION_NAME\]/g, definitions.EXTENSION_NAME) - .replace(/\[LICENSE_CODE\]/g, definitions.LICENSE_CODE) - .replace(/\[LICENSE\]/g, definitions.LICENSE) - .replace(/\[RELEASE_VERSION\]/g, definitions.RELEASE_VERSION) - .replace(/\[TRANSLATION_KEY\]/g, definitions.TRANSLATION_KEY) - .replace(/\[DATE\]/g, releaseDate) - .replace(/\[YEAR\]/g, year); +function discoverFilesToRender(tplDirectory, extensionType) { + const tplPath = `${tplDirectory}/${extensionType}/`; + const absTplPath = `${__dirname}/${tplPath}`; + + return glob.sync( + path.resolve(__dirname, `${tplPath}**/*.@(ini|xml|php|css|js)`), + ) + .map( + (file) => file.replace(absTplPath, '') + ); +} + +function discoverManifestTemplates(tplDirectory, extensionType) { + const tplPath = `${tplDirectory}/${extensionType}/`; + const absTplPath = `${__dirname}/${tplPath}`; + + return glob.sync( + path.resolve(__dirname, `${tplPath}**/*.xml`), + ) + .map( + (file) => file.replace(absTplPath, '') + ); +} + +function resolveExtensionTemplate(tplDirectory, extensionType) { + return path.resolve( + __dirname, + `${tplDirectory}/${extensionType}`, + ); +} function renderTemplates() { const renderTpls = []; - const tplDirectories = [templatesDir, translationsDir]; - const allExtensionTypes = extensionTypesDirs.concat([packageTypeDir]); - tplDirectories.forEach((tplDirectory) => { - allExtensionTypes.forEach((extensionType) => { - const extTplDir = path.resolve( - __dirname, - `${tplDirectory}/${extensionType}`, - ); - const templates = readDirRecursive( - path.resolve(__dirname, `${tplDirectory}/${extensionType}`), - ); + // For templates and translation directories + renderDirectories.forEach((tplDirectory) => { + // For all extension types, including the package + allExtensionTypesDirs.forEach((extensionType) => { + const extTplDir = resolveExtensionTemplate(tplDirectory, extensionType); + const templates = discoverFilesToRender(tplDirectory, extensionType); + // const templatesDeprecated = discoverTemplateDeprecated(tplDirectory, extensionType); + // For each template templates.forEach((file) => { const dest = path.resolve(__dirname, `${extensionType}/${file}`); const item = { @@ -111,6 +268,7 @@ function renderTemplates() { transform: tagTransformation, }; + // Render each template renderTpls.push(item); }); }); @@ -123,7 +281,8 @@ function isPackageType() { let packageMode = false; try { - packageMode = fs.lstatSync(packageDirAbs).isDirectory(); + packageMode = fs.lstatSync(packageDirAbs) + .isDirectory(); } catch (e) { console.log('Package definition not detected.'); } @@ -131,44 +290,67 @@ function isPackageType() { return packageMode; } -function generatePackage() { - const pkgEntries = []; +function declarePackageGeneration() { + const packageDirAbsBar = `${packageDirAbs}/`; // Include all files from the package directory - const pkgFiles = readDirRecursive(packageDirAbs); + const pkgFiles = glob.sync(`${packageDirAbsBar}**/*`) + .map( + (file) => file.replace(packageDirAbsBar, '') + ); - pkgFiles.forEach((file) => { + // Discover extensions to be included + const pkgEntries = pkgFiles.map((file) => { const packageFile = path.resolve(packageDirAbs, file); - const item = { + return { src: packageFile, }; - - pkgEntries.push(item); }); + const folders = new Map(); + // Add all extension types directories into the package extensionTypesDirs.forEach((extensionTypeDir) => { - const extTemplates = readDirRecursive( - path.resolve(__dirname, `${templatesDir}/${extensionTypeDir}`), - ); + const extTemplates = discoverManifestTemplates(templatesDir, extensionTypeDir); extTemplates.forEach((extTemplate) => { + // Exclude discovered files that are not xml + if (!extTemplate.endsWith('.xml')) { + return null; + } + + const extTemplateFile = path.parse(extTemplate) + .base; + + // It is already included, continue with the rest of the files + if (packageIgnoreFiles.includes(extTemplateFile)) { + return; + } + + // Prepare the folder to be included const srcFile = path.resolve( __dirname, `${extensionTypeDir}/${extTemplate}`, ); const srcDir = path.dirname(srcFile); + const distKey = path.basename(srcDir); const item = { src: srcDir, - dist: path.basename(srcDir), + dist: distKey, }; - pkgEntries.push(item); + // Ignore if it has already been included + if (!folders.has(srcDir)) { + folders.set(srcDir, item); + } }); }); + // All discovered folders to be included + pkgEntries.push(...folders.values()); + // Complete the definition of the zip file const outputFile = path.resolve( __dirname, @@ -184,20 +366,18 @@ function generatePackage() { return new ZipFilesPlugin(zipFile); } -function generateZips() { +function declareZipsGeneration() { const zipDirectories = [templatesDir]; const zipPlugins = []; + // For each templates directory to be zipped zipDirectories.forEach((tplDirectory) => { + // For all extension types extensionTypesDirs.forEach((extensionType) => { - const extZipDir = path.resolve( - __dirname, - `${tplDirectory}/${extensionType}`, - ); - const templates = readDirRecursive( - path.resolve(__dirname, `${tplDirectory}/${extensionType}`), - ); + const extZipDir = resolveExtensionTemplate(tplDirectory, extensionType); + const templates = discoverFilesToRender(tplDirectory, extensionType); + // For each template templates.forEach((tplFile) => { const srcFile = path.resolve(__dirname, `${extensionType}/${tplFile}`); const srcDir = path.dirname(srcFile); @@ -212,8 +392,11 @@ function generateZips() { let renamedExtElement = extElement; + // The component must be renamed to the extension alias if (renamedExtElement === 'component') { renamedExtElement = definitions.EXTENSION_ALIAS; + + // The file extension goes to the cli } else if (renamedExtElement === 'file') { renamedExtElement = 'cli'; } @@ -224,16 +407,15 @@ function generateZips() { ); const zipFile = { - entries: [ - { - src: srcDir, - dist: extElement, - }, - ], + entries: [{ + src: srcDir, + dist: extElement, + }], output: outputFile, format: 'zip', }; + // Define the zip const itemZip = new ZipFilesPlugin(zipFile); zipPlugins.push(itemZip); }); @@ -248,18 +430,21 @@ function generateZips() { // Global constant definitions (.env) definitions = loadEnvironmentDefinitions(); +// Clean the development assets before packing +cleanDevAssets(); + // Start clean -removeReleaseDirectory(); +buildPlugins.push(removeReleaseDirectory()); // Render the manifests and translations buildPlugins.push(renderTemplates()); if (isPackageType()) { // Define the package generation - buildPlugins.push(generatePackage()); + buildPlugins.push(declarePackageGeneration()); } else { // Just define the zips with everything - buildPlugins = buildPlugins.concat(generateZips()); + buildPlugins = buildPlugins.concat(declareZipsGeneration()); } // We are ready, Webpack generate!