feat(main): Add base theme: This is the falcon theme out of the box.

This is falcon v3.1.2
This commit is contained in:
2025-11-18 14:04:01 +01:00
parent 3a7f2db331
commit f4f4bcad1d
604 changed files with 49818 additions and 0 deletions

BIN
_dev/webpack/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,4 @@
PORT=3505
SERVER_ADDRESS=host.local
SITE_URL=http://host.local
PUBLIC_PATH=/themes/falcon/assets/

View File

@ -0,0 +1,8 @@
{
"entries": [
"theme",
"product",
"checkout",
"listing"
]
}

View File

@ -0,0 +1,29 @@
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/
module.exports = {
plugins: [
require('autoprefixer')
]
}

View File

@ -0,0 +1,9 @@
exports.module = {
list: [
],
patterns: [
]
}

View File

@ -0,0 +1,22 @@
const { extractScss, extractJs, extractImages, extractFonts, externals, extractVendorsChunks, preloadFonts, resolve } = require('./webpack.parts');
const { merge } = require("webpack-merge");
exports.commonConfig = ({ mode, port, publicPath, siteURL, getOutput, getEntry, entriesArray, stats, devServer }) => (
merge(
{
mode,
entry: getEntry(entriesArray),
output: getOutput({ mode, publicPath, siteURL, port, devServer }),
target: 'web',
},
preloadFonts(),
externals(),
extractScss({ mode }),
extractJs(),
extractImages({ publicPath }),
extractFonts({ publicPath }),
extractVendorsChunks(),
resolve(),
(stats ? { stats } : {})
)
);

View File

@ -0,0 +1,28 @@
const { configureDevServer } = require('./webpack.parts');
const { HotAcceptPlugin } = require('hot-accept-webpack-plugin');
const webpack = require('webpack');
const { merge } = require("webpack-merge");
const devServerConfig = (serverAddress, publicPath, port, siteURL, entriesArray) => {
return {
devServer: configureDevServer(serverAddress, publicPath, port, siteURL),
plugins: [
new webpack.HotModuleReplacementPlugin(),
new HotAcceptPlugin({
test: [
...entriesArray.map(el => `${el}.js`)
]
})
],
optimization: {
runtimeChunk: 'single',
}
}
}
exports.developmentConfig = ({ port, publicPath, serverAddress, siteURL, entriesArray, devServer }) => merge(
{
devtool: "cheap-source-map",
},
devServer ? devServerConfig(serverAddress, publicPath, port, siteURL, entriesArray) : {},
);

View File

@ -0,0 +1,216 @@
const chokidar = require('chokidar');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const FontPreloadPlugin = require('webpack-font-preload-plugin');
exports.configureDevServer = (serverAddress, publicPath, port, siteURL) => ({
allowedHosts: [ serverAddress ],
host: serverAddress,
client: {
logging: 'error',
progress: false,
overlay: {
errors: true,
warnings: false,
},
},
devMiddleware: {
publicPath: publicPath,
writeToDisk: (filePath) => {
return !(/hot-update/.test(filePath));
},
},
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization',
},
hot: true,
liveReload: true,
watchFiles: [
'../../**/*.tpl',
'../../modules/**/*.js',
'../../modules/**/*.css',
],
open: true,
port,
proxy: {
'**': {
target: siteURL,
secure: false,
changeOrigin: true,
}
},
static: {
publicPath: publicPath,
},
});
exports.extractScss = ({mode = 'production'}) => ({
module: {
rules: [{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
config: path.resolve(__dirname, 'postcss.config.js'),
},
}
},
{
loader: 'sass-loader',
options: {
implementation: require('sass'),
},
},
]
}]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].css',
chunkFilename: mode === 'production' ? 'css/[chunkhash].css' : 'css/[id].css',
}),
],
});
exports.extractJs = () => ({
module: {
rules: [
{
test: /swiper\.esm\.js/,
sideEffects: false
},
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'esbuild-loader',
options: {
target: 'es2015'
}
}
},
]
}
});
exports.extractImages = ({ publicPath }) => ({
module: {
rules: [
{
test: /\.(png|jpg|gif|svg)$/,
use: [
{
loader: 'file-loader',
options: {
outputPath: 'img-dist/',
publicPath: publicPath + '/img-dist/',
name: '[contenthash].[ext]',
},
},
],
type: 'javascript/auto',
},
]
}
})
exports.extractFonts = ({ publicPath }) => ({
module: {
rules: [
{
test: /\.(woff|woff2|ttf|eot)$/,
use: [
{
loader: 'file-loader',
options: {
outputPath: 'fonts/',
publicPath: publicPath + '/fonts/',
name: '[name]-[contenthash].[ext]',
},
},
],
type: 'javascript/auto',
}
]
}
})
exports.extractVendorsChunks = () => ({
optimization: {
splitChunks: {
cacheGroups: {
swiper: {
test: /[\\/]node_modules[\\/](swiper|dom7)[\\/]/,
name: 'swipervendor',
filename: 'js/swipervendor.js',
chunks: 'initial',
}
},
},
},
})
exports.cleanDistFolders = () => ({
plugins: [
new CleanWebpackPlugin({
dry: false,
dangerouslyAllowCleanPatternsOutsideProject: true,
cleanOnceBeforeBuildPatterns: [
path.join(__dirname, '../../assets/js/**'),
path.join(__dirname, '../../assets/css/**'),
path.join(__dirname, '../../assets/img-dist/**'),
path.join(__dirname, '../../assets/fonts/**')
],
}),
]
})
exports.externals = () => ({
externals: {
prestashop: 'prestashop',
$: '$',
jquery : 'jQuery'
}
})
exports.preloadFonts = () => ({
plugins: [
new HtmlWebpackPlugin({
filename: 'preload.html',
templateContent: '{{{preloadLinks}}}',
inject: false,
}),
new FontPreloadPlugin({
index: 'preload.html',
extensions: ['woff2'],
filter: /(materialicons|roboto-v20-latin-ext_latin-regular|roboto-v20-latin-ext_latin-700|roboto-v20-latin-ext_latin-500|icomoon)/i,
replaceCallback: ({ indexSource, linksAsString }) => {
return indexSource.replace('{{{preloadLinks}}}', linksAsString);
},
}),
]
})
exports.resolve = () => ({
resolve: {
modules: [
'node_modules',
path.resolve('node_modules')
],
alias: {
'@node_modules': path.resolve(__dirname, '../node_modules'),
'@themeAbstract': path.resolve(__dirname, '../css/abstracts'),
'@css': path.resolve(__dirname, '../css'),
'@js': path.resolve(__dirname, '../js'),
}
},
});

View File

@ -0,0 +1,44 @@
const { ESBuildMinifyPlugin } = require('esbuild-loader');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const PurgeCSSPlugin = require('purgecss-webpack-plugin');
const safeList = require('./purge-safelist');
const glob = require('glob-all');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const { cleanDistFolders } = require('./webpack.parts');
const { merge } = require("webpack-merge");
const plugins = (purge, analyze) => ([
analyze ? new BundleAnalyzerPlugin() : false,
purge ? new PurgeCSSPlugin({
paths: glob.sync([
'js/*.js',
'js/**/*.js',
'../templates/**/*.tpl',
'../modules/**/*.tpl',
'../modules/**/*.js'
]),
safelist: safeList.list,
safelistPatterns: safeList.patterns
})
: false
].filter(el => el && el));
exports.productionConfig = ({ purge, analyze }) => (
merge(
{
devtool: 'hidden-source-map',
optimization: {
minimize: true,
minimizer: [
new ESBuildMinifyPlugin({
target: 'es2015',
format: 'iife'
}),
new CssMinimizerPlugin()
],
},
plugins: plugins(purge, analyze)
},
cleanDistFolders()
)
);

View File

@ -0,0 +1,79 @@
const path = require('path');
const themeDev = path.resolve(__dirname, '../../_dev');
const packageJson = require('../package.json');
const glob = require('glob-all');
const getWorkspacesFromPackageJson = (packageJson) => packageJson.workspaces ?? [];
let entriesArray = null;
const getEntriesArray = () => {
if (!entriesArray) {
entriesArray = require('../webpack/entries.json').entries
}
return entriesArray;
}
exports.getEnvData = ({env, options, webpackVars}, initConfig) => {
const envFileName = typeof env.envFile != 'undefined' ? env.envFile : '.env';
const envResult = require('dotenv').config({ path: `./webpack/${envFileName}` });
const {
PORT: port,
PUBLIC_PATH: publicPath,
SERVER_ADDRESS: serverAddress,
SITE_URL: siteURL
} = process.env;
if (envResult.error) {
console.error('\x1b[41m\x1b[37m%s\x1b[0m', envResult.error + ' Your .env file not exits. Read installation documentation for more info https://github.com/Oksydan/modern-prestashop-starter-theme#installation.');
process.exit(1);
}
return initConfig({
mode: options.mode,
purge: env.purge ? env.purge : false,
analyze: env.analyze ? env.analyze : false,
devServer: env.devServer ? env.devServer : false,
stats: env.stats ? env.stats : false,
port,
publicPath,
serverAddress,
siteURL,
...webpackVars
})
}
exports.webpackVars = {
themeDev,
entriesArray: getEntriesArray(),
getEntry: (entries) => {
const resultEntries = {};
const workspaces = getWorkspacesFromPackageJson(packageJson);
for (const entry of entries) {
const extraEntries = [];
for (const workspace of workspaces) {
extraEntries.push(...glob.sync(`${workspace}/src/js/${entry}/index.js`));
extraEntries.push(...glob.sync(`${workspace}/src/css/${entry}/index.scss`));
}
resultEntries[entry] = [
path.resolve(themeDev, `./js/${entry}.js`),
path.resolve(themeDev, `./css/${entry}.scss`),
...extraEntries,
]
}
return resultEntries;
},
getOutput: ({ mode, publicPath, siteURL, port, devServer }) => ({
filename: 'js/[name].js',
chunkFilename: mode === 'production' ? 'js/[chunkhash].js' : 'js/[id].js',
path: path.resolve(themeDev, '../assets'),
publicPath: !devServer ? publicPath : siteURL + ':' + port + publicPath,
pathinfo: false,
}),
}