Skip to content

Instantly share code, notes, and snippets.

@oliverthiele
Created November 6, 2023 08:57
Show Gist options
  • Save oliverthiele/6298902d5192aa86f24682a3afbd0d9f to your computer and use it in GitHub Desktop.
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
'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