diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2a66d0d --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.vscode +node_modules/ +package-lock.json +dist/ +.DS_Store \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..4f437f7 --- /dev/null +++ b/package.json @@ -0,0 +1,42 @@ +{ + "name": "portfolio", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node ./server.js", + "build": "NODE_ENV=production webpack --config webpack.prod.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/core": "^7.17.5", + "@babel/preset-env": "^7.16.11", + "@babel/preset-react": "^7.16.7", + "babel-loader": "^8.2.3", + "clean-webpack-plugin": "^4.0.0", + "css-minimizer-webpack-plugin": "^3.4.1", + "express": "^4.17.3", + "html-critical-webpack-plugin": "^2.1.0", + "html-loader": "^3.1.0", + "html-webpack-plugin": "^5.5.0", + "image-minimizer-webpack-plugin": "^3.2.3", + "mini-css-extract-plugin": "^2.6.0", + "sass": "^1.49.9", + "sass-loader": "^12.6.0", + "style-loader": "^3.3.1", + "webpack": "^5.70.0", + "webpack-cli": "^4.9.2", + "webpack-dev-middleware": "^5.3.1", + "webpack-hot-middleware": "^2.25.1", + "webpack-merge": "^5.8.0" + }, + "dependencies": { + "@react-three/fiber": "^7.0.26", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "three": "^0.138.3" + } +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..5831149 --- /dev/null +++ b/server.js @@ -0,0 +1,32 @@ +const path = require( 'path' ); +const express = require( 'express' ); +const webpack = require( 'webpack' ); +const webpackDevMiddleware = require( 'webpack-dev-middleware' ); +const webpackHotMiddleware = require( 'webpack-hot-middleware' ); + +const app = express(); +const config = require('./webpack.dev.js'); +const compiler = webpack(config); + +// Tell express to use the webpack-dev-middleware and use the webpack.config.js +// configuration file as a base. +app.use( + webpackDevMiddleware(compiler, { + publicPath: config.output.publicPath, + writeToDisk: true, + }) +); + +app.use( webpackHotMiddleware(compiler, { + noInfo: true, + quiet: true +}) ); + +app.get( '/*', function(req, res) { + res.sendFile( 'index.html', {root: path.join( __dirname, './dist/')}); +} ); + +// Serve the files on port 3000. +app.listen(3000, function () { + console.log('Example app listening on port 3000!\n'); +}); diff --git a/src/components/App.jsx b/src/components/App.jsx new file mode 100644 index 0000000..c5d2f6b --- /dev/null +++ b/src/components/App.jsx @@ -0,0 +1,39 @@ +import '../styles/styles.scss'; +import { useRef, useState } from 'react'; +import { Canvas, useFrame } from '@react-three/fiber'; + +const Box = (props) => { + // This reference gives us direct access to the THREE.Mesh object + const ref = useRef() + // Hold state for hovered and clicked events + const [hovered, hover] = useState(false) + const [clicked, click] = useState(false) + // Subscribe this component to the render-loop, rotate the mesh every frame + useFrame((state, delta) => (ref.current.rotation.x += 0.01)) + // Return the view, these are regular Threejs elements expressed in JSX + return ( + click(!clicked)} + onPointerOver={(event) => hover(true)} + onPointerOut={(event) => hover(false)}> + + + + ) +}; + +const App = () => { + return ( + + + + + + + ); +}; + +export default App; \ No newline at end of file diff --git a/src/scripts/app.js b/src/scripts/app.js new file mode 100644 index 0000000..be89ccc --- /dev/null +++ b/src/scripts/app.js @@ -0,0 +1,10 @@ +import { render } from 'react-dom'; +import App from '../components/App'; + +render( + , + document.getElementById("root") ); + +if (module['hot']) { + module['hot'].accept(); +} diff --git a/src/styles/styles.scss b/src/styles/styles.scss new file mode 100644 index 0000000..69c38a8 --- /dev/null +++ b/src/styles/styles.scss @@ -0,0 +1,3 @@ +main { + background-color: black; +} \ No newline at end of file diff --git a/src/views/index.html b/src/views/index.html new file mode 100644 index 0000000..1fa7e21 --- /dev/null +++ b/src/views/index.html @@ -0,0 +1,12 @@ + + + + + + + Document + + +
+ + \ No newline at end of file diff --git a/webpack.common.js b/webpack.common.js new file mode 100644 index 0000000..88b8295 --- /dev/null +++ b/webpack.common.js @@ -0,0 +1,125 @@ +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const MiniCSSExtractPlugin = require('mini-css-extract-plugin'); +const path = require('path'); + +// let htmlPageNames = []; +// let multipleHtmlPlugins = htmlPageNames.map(name => { +// return new HtmlWebpackPlugin({ +// template: `./src/views/${name}.html`, // relative path to the HTML files +// filename: `${name}.html`, // output HTML files +// chunks: [`${name}`] // respective JS files +// }) +// }); + +module.exports = { + resolve: { + extensions: ['.js', '.jsx'], + }, + module: { + rules: [ + // HTML + { + test: /\.html$/, + use: { + loader: 'html-loader', + }, + }, + + // JS + { + test: /\.jsx?$/i, + exclude: /node_modules/, + include: path.resolve(__dirname, 'src'), + use: { + loader: 'babel-loader', + options: { + presets: [ + '@babel/preset-env', + ['@babel/preset-react', { runtime: 'automatic' }], + ], + }, + }, + }, + + // CSS + { + test: /\.s[ac]ss$/i, + use: [ + MiniCSSExtractPlugin.loader, + 'css-loader', + 'postcss-loader', + 'resolve-url-loader', + 'sass-loader', + ], + }, + + // Images + + { + test: /\.(png|svg|jpg|jpeg|gif)$/i, + type: 'asset', + }, + + // { + // test: /\.(jpg|png|gif|svg)$/, + // use: { + // loader: 'url-loader' + // } + // }, + + // { + // test: /\.(jpg|png|gif|svg)$/, + // use: + // [ + // { + // loader: 'file-loader', + // options: + // { + // outputPath: './assets/images/' + // } + // } + // ] + // }, + + // Fonts + + { + test: /\.(woff|woff2|eot|ttf|otf)$/i, + type: 'asset/resource', + }, + + // { + // test: /\.(ttf|eot|woff|woff2)$/, + // use: + // [ + // { + // loader: 'file-loader', + // options: + // { + // outputPath: './assets/fonts/' + // } + // } + // ] + // } + ], + }, + plugins: [ + new HtmlWebpackPlugin({ + template: path.resolve(__dirname, './src/views/index.html'), + inject: 'body', + }), + ], + optimization: { + moduleIds: 'deterministic', + runtimeChunk: 'single', + splitChunks: { + cacheGroups: { + vendor: { + test: /[\\/]node_modules[\\/]/, + name: 'vendors', + chunks: 'all', + }, + }, + }, + }, +}; diff --git a/webpack.dev.js b/webpack.dev.js new file mode 100644 index 0000000..14b674a --- /dev/null +++ b/webpack.dev.js @@ -0,0 +1,60 @@ +const { merge } = require('webpack-merge'); +const webpack = require( 'webpack' ); +const commonConfiguration = require('./webpack.common.js'); +const MiniCSSExtractPlugin = require('mini-css-extract-plugin'); +const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin'); +const path = require('path'); + +const hotMiddlewareScript = + 'webpack-hot-middleware/client?reload=true'; + +module.exports = merge(commonConfiguration, { + mode: 'development', + entry: { + portfolio: [hotMiddlewareScript, './src/scripts/app.js'], + }, + output: { + filename: 'js/[name].[contenthash].js', + path: path.resolve(__dirname, './dist'), + publicPath: '/', + clean: true, + // hotUpdateChunkFilename: 'hot/hot-update.js', + // hotUpdateMainFilename: 'hot/hot-update.json' + }, + devtool: 'source-map', + plugins: [ + new webpack.HotModuleReplacementPlugin(), + new MiniCSSExtractPlugin({ + filename: 'css/[name].[contenthash].css', + }), + // new ImageMinimizerPlugin({ + // minimizerOptions: { + // // Lossless optimization with custom option + // // Feel free to experiment with options for better result for you + // plugins: [ + // ['gifsicle', { interlaced: true }], + // ['jpegtran', { progressive: true }], + // ['optipng', { optimizationLevel: 5 }], + // // Svgo configuration here https://github.com/svg/svgo#configuration + // [ + // 'svgo', + // { + // plugins: [ + // { + // name: 'removeViewBox', + // active: false, + // }, + // { + // name: 'addAttributesToSVGElement', + // params: { + // attributes: [{ xmlns: 'http://www.w3.org/2000/svg' }], + // }, + // }, + // ], + // }, + // ], + // ], + // }, + // }), + ] +}); diff --git a/webpack.prod.js b/webpack.prod.js new file mode 100644 index 0000000..3e99312 --- /dev/null +++ b/webpack.prod.js @@ -0,0 +1,78 @@ +const { merge } = require('webpack-merge'); +const commonConfiguration = require('./webpack.common.js'); +const { CleanWebpackPlugin } = require('clean-webpack-plugin'); +const path = require('path'); +const MiniCSSExtractPlugin = require('mini-css-extract-plugin'); +const HtmlCriticalPlugin = require('html-critical-webpack-plugin'); +const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); +const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin'); + +module.exports = merge(commonConfiguration, { + mode: 'production', + entry: { + popshop: path.resolve(__dirname, './src/scripts/app.js'), + }, + devtool: 'source-map', + output: { + filename: 'js/[name].[contenthash].js', + path: path.resolve(__dirname, './dist'), + clean: true, + assetModuleFilename: 'assets/images/[name][ext][query]', + }, + plugins: [ + new CleanWebpackPlugin(), + new MiniCSSExtractPlugin({ + filename: 'css/[name].[contenthash].css', + }), + // new ImageMinimizerPlugin({ + // minimizerOptions: { + // // Lossless optimization with custom option + // // Feel free to experiment with options for better result for you + // plugins: [ + // ['gifsicle', { interlaced: true }], + // ['jpegtran', { progressive: true }], + // ['optipng', { optimizationLevel: 5 }], + // // Svgo configuration here https://github.com/svg/svgo#configuration + // [ + // 'svgo', + // { + // plugins: [ + // { + // name: 'removeViewBox', + // active: false, + // }, + // { + // name: 'addAttributesToSVGElement', + // params: { + // attributes: [{ xmlns: 'http://www.w3.org/2000/svg' }], + // }, + // }, + // ], + // }, + // ], + // ], + // }, + // }), + new HtmlCriticalPlugin({ + base: path.join(path.resolve(__dirname), 'dist/'), + src: 'index.html', + dest: 'index.html', + inline: true, + minify: true, + extract: true, + width: 375, + height: 565, + penthouse: { + blockJSRequests: false, + }, + }), + ], + optimization: { + minimize: true, + minimizer: [ + new CssMinimizerPlugin({ + parallel: true, + }), + ], + }, +});