Created
November 6, 2023 08:57
-
-
Save oliverthiele/6298902d5192aa86f24682a3afbd0d9f to your computer and use it in GitHub Desktop.
My webpack config for TYPO3 v11 and v12. Works with public/_asset/[hash] symlinks in TYPO3 v12
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict' | |
// Imports | |
const {VueLoaderPlugin} = require("vue-loader"); | |
const fs = require('fs'); | |
const path = require('path'); | |
const autoprefixer = require('autoprefixer'); | |
const CopyWebpackPlugin = require("copy-webpack-plugin"); | |
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); | |
const webpack = require('webpack'); | |
// Target TYPO3 extension | |
const DEFAULT_TARGET_PACKAGE = 'oliverthiele/ot-febuild'; | |
// --------------------- | |
// Set via composer.json | |
let TYPO3_VERSION; | |
let targetExtensionDirectory; | |
let targetExtensionVendor; | |
let targetPackage; | |
/** | |
* Get entry points | |
*/ | |
// Get all entry points from Build/Default/EntryPoints/ | |
const entryPointsPath = path.resolve(__dirname, 'EntryPoints'); | |
const entryFiles = fs.readdirSync(entryPointsPath); | |
const entry = {}; | |
entryFiles.forEach(file => { | |
const fileNameWithoutExt = path.parse(file).name; | |
entry[fileNameWithoutExt] = path.join(entryPointsPath, file); | |
}); | |
/** | |
* Get the revision of the Sprites.svg file. A newer revision will update the local storage. | |
*/ | |
// Pfad zur SVG-Datei | |
const filePath = './Resources/Assets/Website/Icons/Sprites.svg'; | |
// Lese Dateiinformationen | |
const stats = fs.statSync(filePath); | |
// Ermittle den Zeitstempel der letzten Änderung | |
const timestamp = stats.mtime.getTime(); | |
console.log(timestamp); | |
const determineVendorAndExtension = (env) => { | |
const fullVendorExtension = env?.TARGET_PACKAGE ?? DEFAULT_TARGET_PACKAGE; | |
const [vendor, assetExtension] = fullVendorExtension.split('/'); | |
targetExtensionVendor = vendor; | |
targetPackage = fullVendorExtension; | |
if (TYPO3_VERSION === 11) { | |
// string replace "-" -> "_" | |
// (TYPO3 v11 will use e.g. public/typo3conf/ext/my_extensionkey instead of vendor/my-namespace/my-extensionkey | |
targetExtensionDirectory = assetExtension.replace(/-/g, '_'); | |
} else { | |
targetExtensionDirectory = assetExtension; | |
} | |
}; | |
// Get TYPO3 version from composer.json | |
try { | |
const composerJsonPath = path.resolve(__dirname, '../../composer.json'); | |
const composerJsonData = fs.readFileSync(composerJsonPath, 'utf8'); | |
const composerObj = JSON.parse(composerJsonData); | |
const typo3VersionString = composerObj.require['typo3/cms-core']; | |
const match = typo3VersionString.match(/^(\^|~)?(\d+)/); | |
if (match && match[2]) { | |
TYPO3_VERSION = parseInt(match[2], 10); | |
console.log(`TYPO3-Version: ${TYPO3_VERSION}`); | |
} else { | |
console.error('TYPO3 Version konnte nicht ermittelt werden. Beende den Prozess.'); | |
process.exit(1); | |
} | |
} catch (error) { | |
console.error('Fehler beim Lesen der composer.json:', error); | |
process.exit(1); | |
} | |
/** | |
* * This function determines the path for TYPO3 assets depending on the TYPO3 version. | |
* * For version 11 a static path is returned, while for version 12 the folder structure is searched, | |
* * to determine the path dynamically. | |
* | |
* @returns {string|null} | |
*/ | |
const determineTypo3AssetUrl = () => { | |
if (TYPO3_VERSION === 11) { | |
return `/typo3conf/ext/${targetExtensionDirectory}/Resources/Public/Assets/`; | |
} | |
if (TYPO3_VERSION === 12) { | |
const assetDir = path.resolve(__dirname, '../../public/_assets'); | |
const files = fs.readdirSync(assetDir); | |
for (const file of files) { | |
const fullPath = path.join(assetDir, file); | |
const linkTarget = path.resolve(fs.readlinkSync(fullPath)); | |
if (linkTarget.endsWith(`/${targetExtensionDirectory}/Resources/Public`)) { | |
const lastSegment = path.basename(fullPath); | |
return `/_assets/${lastSegment}/Assets/`; | |
} | |
} | |
} | |
return null; // Symlink was not found | |
}; | |
const determinePublicPath = (prod, watch) => { | |
if (!prod && !watch) { | |
console.log('Return /'); | |
return '/'; | |
} | |
return determineTypo3AssetUrl() ?? '/'; | |
} | |
module.exports = (env, argv) => { | |
const mode = argv.mode === 'production' ? 'production' : 'development'; | |
const prod = mode === 'production'; | |
const watch = !!(env.watch ?? false); | |
console.log(`Production Mode: ${prod}`); | |
// get vendor and extension directory | |
determineVendorAndExtension(env); | |
// FE URL (/_assets/... or /typo3conf/ext/my_extkey/...) | |
const typo3AssetsUrl = determineTypo3AssetUrl() ?? '/'; | |
// console.log(`targetExtensionDirectory: ${targetExtensionDirectory}`); | |
console.log(`Asset URL: ${typo3AssetsUrl}`); | |
const publicPath = determinePublicPath(prod, watch) ?? '/'; | |
console.log(`Public Path: ${publicPath}`); | |
// The dist path is used for the file watcher | |
let distPath = ''; | |
if (TYPO3_VERSION <= 11) { | |
// todo check if secure_web is installed | |
// "private" directory only, if secure_web is installed! | |
distPath = `../../private${publicPath}`; | |
} else { | |
distPath = env.distPath ?? `../../vendor/${targetPackage}/Resources/Public/Assets`; | |
} | |
return { | |
mode: mode, | |
devtool: !prod ? 'source-map' : false, | |
watch: watch, | |
entry: entry, | |
output: { | |
publicPath: publicPath, | |
path: path.resolve(__dirname, distPath), | |
filename: 'JavaScript/[name].js', | |
}, | |
devServer: { | |
static: path.resolve(__dirname, 'dist'), port: 8080, hot: true | |
}, | |
plugins: [ | |
new webpack.DefinePlugin({ | |
'process.env.TYPO3_ASSETS_URL': JSON.stringify(typo3AssetsUrl), | |
'process.env.SVG_REVISION': JSON.stringify(timestamp), | |
'__VUE_OPTIONS_API__': JSON.stringify(true), // Enable Vue Options API in dev/prod | |
'__VUE_PROD_DEVTOOLS__': JSON.stringify(!prod) // Disable devtools in production | |
}), | |
new CopyWebpackPlugin({ | |
patterns: [ | |
{ | |
from: './node_modules/@fortawesome/fontawesome-pro/svgs/', to: 'Website/SVG' | |
}, { | |
from: '../Default/Resources/Assets/' | |
} | |
], | |
}), | |
new MiniCssExtractPlugin({ | |
filename: 'Styles/[name].css' | |
}), | |
new VueLoaderPlugin() | |
], | |
module: { | |
rules: [ | |
{ | |
test: /\.(s[ac]ss)$/, | |
use: [MiniCssExtractPlugin.loader, { | |
loader: 'css-loader', options: { | |
sourceMap: !prod | |
} | |
}, { | |
loader: 'postcss-loader', options: { | |
postcssOptions: { | |
plugins: [autoprefixer] | |
}, sourceMap: !prod // aktiviere Source Map nur im Development-Modus | |
} | |
}, { | |
loader: 'sass-loader', options: { | |
sourceMap: !prod | |
} | |
}] | |
}, | |
{ | |
test: /\.vue$/, // Hinzugefügt | |
loader: 'vue-loader' // Hinzugefügt | |
} | |
] | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment