Some structure changes; added naviagation; added helpers

This commit is contained in:
2021-10-07 12:13:21 +03:00
parent 505f4dffc0
commit dc19bb1fa5
14 changed files with 417 additions and 208 deletions

3
.gitignore vendored
View File

@@ -86,7 +86,6 @@ out
# Nuxt.js build / generate output # Nuxt.js build / generate output
.nuxt .nuxt
dist
# Gatsby files # Gatsby files
.cache/ .cache/
@@ -204,7 +203,6 @@ out
# Nuxt.js build / generate output # Nuxt.js build / generate output
.nuxt .nuxt
dist
# Gatsby files # Gatsby files
.cache/ .cache/
@@ -237,3 +235,4 @@ dist
.yarn/install-state.gz .yarn/install-state.gz
.pnp.* .pnp.*
/.trash/

View File

@@ -9,21 +9,29 @@
"srv": "cross-env SOURCE_MAP_ENV=true webpack serve", "srv": "cross-env SOURCE_MAP_ENV=true webpack serve",
"watch": "webpack --watch", "watch": "webpack --watch",
"build": "cross-env NODE_ENV=production webpack", "build": "cross-env NODE_ENV=production webpack",
"build-dev": "cross-env SOURCE_MAP_ENV=true webpack" "build-dev": "cross-env SOURCE_MAP_ENV=true NODE_ENV=production webpack"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"@babel/core": "^7.15.5", "@babel/core": "^7.15.5",
"@babel/plugin-proposal-class-properties": "^7.14.5",
"@babel/preset-env": "^7.15.6", "@babel/preset-env": "^7.15.6",
"babel-loader": "^8.2.2", "babel-loader": "^8.2.2",
"clean-webpack-plugin": "^4.0.0", "clean-webpack-plugin": "^4.0.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"css-loader": "^6.3.0", "css-loader": "^6.3.0",
"css-minimizer-webpack-plugin": "^3.1.1",
"css-mqpacker-webpack-plugin": "^0.12.1",
"glob": "^7.2.0",
"html-webpack-plugin": "^5.3.2", "html-webpack-plugin": "^5.3.2",
"mini-css-extract-plugin": "^2.3.0", "mini-css-extract-plugin": "^2.3.0",
"terser-webpack-plugin": "^5.2.4", "mini-svg-data-uri": "^1.3.3",
"postcss-loader": "^6.1.1",
"postcss-preset-env": "^6.7.0",
"purgecss-webpack-plugin": "^4.0.3",
"style-loader": "^3.3.0",
"webpack": "^5.56.1", "webpack": "^5.56.1",
"webpack-cli": "^4.8.0", "webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.3.1" "webpack-dev-server": "^4.3.1"

View File

@@ -9,6 +9,7 @@
], ],
"querySelectors": { "querySelectors": {
"console": "#appConsole", "console": "#appConsole",
"navTabs": "#asSwitcher",
"modal": ".modal", "modal": ".modal",
"notesGrid": "#notesGrid", "notesGrid": "#notesGrid",
"noteEditForm": "#noteEditForm", "noteEditForm": "#noteEditForm",
@@ -22,8 +23,8 @@
}, },
"gridOrder": { "gridOrder": {
"notes": [ "notes": [
"title",
"createdAt", "createdAt",
"title",
"category", "category",
"content", "content",
"dates", "dates",

80
src/helpers.js Normal file
View File

@@ -0,0 +1,80 @@
export const env = require('./env.json')
export const msgBox = document.getElementById(env.querySelectors.console.slice(1))
/**
*
* @type {{}}
*/
const monthsNamesFull = env.lists.monthsFull
/**
*
* @param string
* @returns {string}
*/
export const ucWords = string => {
return string.split('_').map(word => word.charAt(0).toUpperCase() + word.substr(1).toLowerCase()).join(' ')
}
/**
*
* @param date
* @returns {string}
*/
export const formatDate = date => {
const dt = new Date(date)
return [monthsNamesFull[dt.getMonth()], dt.getDate(), `, ${dt.getFullYear()}`].join(' ')
}
/**
*
* @param str
* @returns {*[]}
*/
export const findDates = str => {
const dates = []
const regex = new RegExp('[0-3]?[0-9].[0-3]?[0-9].(?:[0-9]{2})?[0-9]{2}', 'mg')
for (const match of (str || '').matchAll(regex)) {
dates.push(match[0])
}
return dates
}
/**
*
* @param args
* @returns {*}
*/
export const createNode = (...args) => {
const [tag, attrs, content, callback] = args
const node = document.createElement(tag)
for (const [key, value] of Object.entries(attrs || [])) {
node.setAttribute(key, value)
}
node.innerHTML = content || ''
return node
}
/**
*
* @param node
*/
export const destroyNode = (node) => {
if (!(node instanceof Node) && typeof node === 'string') {
node = document.querySelector(node)
}
try {
node.remove()
} catch (e) {}
}

View File

@@ -8,6 +8,5 @@
<body> <body>
<div id="appConsole" class="uk-container"></div> <div id="appConsole" class="uk-container"></div>
<div id="app" class="uk-container"></div> <div id="app" class="uk-container"></div>
</body> </body>
</html> </html>

View File

@@ -1,42 +1,13 @@
import env from './env.json' import { env } from './helpers'
import './styles/uikit.min.css' import './styles/uikit.min.css'
import './styles/main.css' import './styles/main.css'
import DOMController from './modules/DOMController' import DOMController from './modules/DOMController'
//localStorage.clear()
window.env = env
const monthsNamesFull = window.env.lists.monthsFull
/**
*
* @param str
* @returns {*[]}
* @private
*/
window.___findDates = str => {
const dates = []
const regex = new RegExp('[0-3]?[0-9].[0-3]?[0-9].(?:[0-9]{2})?[0-9]{2}', 'mg')
for (const match of (str || '').matchAll(regex)) {
dates.push(match[0])
}
return dates
}
window.___formatDate = date => {
const dt = new Date(date)
return [monthsNamesFull[dt.getMonth()], dt.getDate(), `, ${dt.getFullYear()}`].join(' ')
}
const app = document.getElementById('app') const app = document.getElementById('app')
const view = new DOMController(app) window.env = env
//console.log(dt, dt.match(/^(((2000|2400|2800|((19|2[0-9])(0[48]|[2468][048]|[13579][26])))-02-29)|(((19|2[0-9])[0-9]{2})-02-(0[1-9]|1[0-9]|2[0-8]))|(((19|2[0-9])[0-9]{2})-(0[13578]|10|12)-(0[1-9]|[12][0-9]|3[01]))|(((19|2[0-9])[0-9]{2})-(0[469]|11)-(0[1-9]|[12][0-9]|30)))T([01][0-9]|[2][0-3]):[0-5][0-9]:[0-5][0-9]\.[0-9]{3}Z$/) ) new DOMController(app)
//localStorage.clear()
/*NotesAPI.saveNote({ /*NotesAPI.saveNote({
//id: 441512, //id: 441512,
@@ -47,4 +18,7 @@ const view = new DOMController(app)
})*/ })*/
//NotesAPI.deleteNote(441512) //NotesAPI.deleteNote(441512)
//console.log(NotesAPI.getNotes()) //console.log(NotesAPI.getNotes())
/*
1/1/11 or 1.1.11 or 1-1-11 01/01/11 or 01.01.11 or 01-01-11 01/01/2011 or 01.01.2011 or 01-01-2011 : true 01/1/2011 or 01.1.2011 or 01-1-2011 : true 1/11/2011 or 1.11.2011 or 1-11-2011 1/11/11 or 1.11.11 or 1-11-11 11/1/11 or 11.1.11 or 11-1-11*/

View File

@@ -1,4 +1,5 @@
import Templates from '../templates/Views' import Templates from '../templates/Views'
import { msgBox, createNode, destroyNode, findDates, formatDate, ucWords } from '../helpers'
import NotesAPI from './NotesAPI' import NotesAPI from './NotesAPI'
export default class DOMController { export default class DOMController {
@@ -13,15 +14,29 @@ export default class DOMController {
} }
this.btnCreateNote = this.root.querySelector(window.env.querySelectors.btnCreateNote) this.btnCreateNote = this.root.querySelector(window.env.querySelectors.btnCreateNote)
this.createNotesGrid(window.env.prePopulateAmount || 0) this.createNotesGrid(window.env.prePopulateAmount || 0)
this.listener([ this.listener([
[ [this.btnCreateNote, 'click', (this.btnCreateNote.dataset.action || 'showError'), { legend: 'Create Note' }],
this.btnCreateNote, 'click', (this.btnCreateNote.dataset.action || 'showError'), {
legend: 'Create Note',
}],
]) ])
this.navTabs()
}
navTabs () {
const navTabs = document.querySelectorAll(window.env.querySelectors.navTabs + ' > li > a')
for (let i = 0; i < navTabs.length; ++i) {
this.listener([navTabs[i], 'click', 'createNotesGridByRoute', navTabs[i]])
}
}
/**
*
*/
showError () {
DOMController.alert('Error occurred :(', 3000, 3)
} }
/** /**
@@ -29,28 +44,68 @@ export default class DOMController {
* @param msg * @param msg
* @param fadeOut * @param fadeOut
* @param type * @param type
* @param womb
*/ */
static alert (msg = '', fadeOut = 0, type = 0) { static alert (msg = 'Alert without message', fadeOut = 0, type = 0, womb = null) {
const alertTypes = window.env.alertTypes const alertTypes = window.env.alertTypes
const alert = this.createNode('div', { class: `uk-alert-${alertTypes[type]}`, 'uk-alert': null }, const alert = createNode('div', { class: `uk-alert-${alertTypes[type]} uk-padding-small`, 'uk-alert': null }, msg)
`<a class="uk-alert-close" uk-close></a>\n` + msg)
alert.style.display = 'block' alert.style.display = 'block'
document.getElementById(window.env.querySelectors.console.slice(1)).appendChild(alert) womb instanceof Element ? womb.appendChild(alert) : msgBox.appendChild(alert)
if (fadeOut > 0) {
setTimeout(() => {
alert.remove()
}, fadeOut)
}
} }
createNotesGrid (len, archived = false) { /**
*
* @param node
*/
noteToTrash (node) {
const row = node.closest('tr')
NotesAPI.deleteNote(parseInt(row.dataset.id, 10))
row.classList.add('as-removing')
setTimeout(() => {
row.remove()
this.createNotesGrid(window.env.prePopulateAmount || 0)
}, 3000)
}
createNotesGridByRoute (navTab) {
const route = new URL( navTab.href ).pathname.slice(1).split('\/')[0]
document.querySelector('li.nav-tab.uk-active').classList.remove('uk-active')
navTab.closest('li').classList.add( 'uk-active' )
switch ( route ){
case 'all': this.createNotesGrid( 0, null ); break;
case '': this.createNotesGrid( window.env.prePopulateAmount || 0 ); break;
case 'archived': this.createNotesGrid( window.env.prePopulateAmount || 0, true ); break;
default: console.log(route)
}
}
/**
*
* @param len
* @param archived
*/
createNotesGrid (len = window.env.prePopulateAmount || 0, archived = false) {
const notes = NotesAPI.getNotesSanitized(len, archived) const notes = NotesAPI.getNotesSanitized(len, archived)
if (notes.length === 0) { return } if (notes.length === 0) { return }
const nodeToListen = []
const notesGrid = document.querySelector(window.env.querySelectors.notesGrid + ' tbody') const notesGrid = document.querySelector(window.env.querySelectors.notesGrid + ' tbody')
notesGrid.innerHTML = '' notesGrid.innerHTML = ''
notes.forEach(el => { notes.forEach(el => {
const row = DOMController.createNode('tr') const row = createNode('tr')
row.dataset.id = el.id row.dataset.id = el.id
window.env.gridOrder.notes.forEach(key => { window.env.gridOrder.notes.forEach(key => {
@@ -59,14 +114,24 @@ export default class DOMController {
switch (key) { switch (key) {
case 'createdAt': case 'createdAt':
value = ___formatDate(value) value = formatDate(value)
break
case 'category':
value = ucWords( value )
break
case 'title':
//attrs.class = 'uk-text-truncate'
attrs.class = 'uk-text-bold'
break break
case 'content': case 'content':
attrs.class = 'uk-text-truncate'
//attrs.class = 'uk-text-truncate'
//attrs.title = value
break break
case 'dates': case 'dates':
attrs.class = 'uk-text-truncate' value = findDates(el.content).join(', ')
value = ___findDates(el.content).join(', ') //attrs.class = 'uk-text-truncate'
//attrs.title = value
break break
case 'edit' : case 'edit' :
attrs.class = 'icon icon-edit grid-control' attrs.class = 'icon icon-edit grid-control'
@@ -78,15 +143,14 @@ export default class DOMController {
break break
case 'delete' : case 'delete' :
attrs.class = 'icon icon-delete grid-control' attrs.class = 'icon icon-delete grid-control'
attrs['data-action'] = 'toTrash' attrs['data-action'] = 'noteToTrash'
break break
} }
const td = DOMController.createNode('td', attrs, value) const td = createNode('td', attrs, value)
if (key.match(/^(edit|archive|delete)$/)) { if (key.match(/^(edit|archive|delete)$/)) {
nodeToListen.push(td) this.listener([td, 'click', td.dataset.action, td])
this.listener([td, 'click', td.dataset.action])
} }
row.appendChild(td) row.appendChild(td)
@@ -96,89 +160,6 @@ export default class DOMController {
}) })
} }
showError () {
console.error('Error occurred :(')
}
static createNode (...args) {
const [tag, attrs, content, callback] = args
const node = document.createElement(tag)
for (const [key, value] of Object.entries(attrs || [])) {
node.setAttribute(key, value)
}
node.innerHTML = content || ''
return node
}
destroyNode (selector) {
if (!(selector instanceof Node) && typeof selector === 'string') {
selector = document.querySelector(selector)
}
try {
selector.remove()
} catch (e) {}
}
getEditForm (args = {}) {
const node = DOMController.createNode('div', { class: window.env.querySelectors.modal.slice(1) },
Templates.templates.editForm)
if (args.legend) {
node.querySelector('legend').innerHTML = args.legend
}
this.root.appendChild(node)
node.style.display = 'block'
this.listener([
[node.querySelector(window.env.querySelectors.btnDestroyModal), 'click', 'destroyNode', node],
[node.querySelector('form'), 'submit', NotesAPI.saveNote],
])
}
listener (els) {
els[0] instanceof Element ? this.on(els) : els.forEach(el => this.on(el))
}
/**
*
* @param args //selector, event, method, params
*/
on (args) {
const [selector, event, method] = args
if (!(selector instanceof Element)) {
return
}
try {
selector.addEventListener(event, evt => {
const params = args[3] || {}
if (evt.type === 'submit') {
evt.preventDefault()
}
if (typeof this[method] === 'function') {
this[method](params)
} else if (typeof method === 'function') {
method(params)
}
})
} catch (err) {
console.error(err)
}
}
/** /**
* *
* @param selector * @param selector
@@ -194,4 +175,69 @@ export default class DOMController {
return data return data
} }
}
/**
*
* @param els
* @returns {void|*}
*/
listener (els) { return els[0] instanceof Element ? this.on(els) : els.forEach(el => this.on(el)) }
/**
*
* @param args //selector, event, method, params
*/
on (args) {
const [selector, event, method] = args
if (!(selector instanceof Element)) {
return
}
try {
selector.addEventListener(event, evt => {
const params = args[3] || {}
if (selector.tagName === 'A') {
evt.preventDefault()
}
if (evt.type === 'submit') {
evt.preventDefault()
setTimeout(() => {
this.createNotesGrid(window.env.prePopulateAmount || 0)
}, 300)
}
if (typeof this[method] === 'function') {
this[method](params)
} else if (typeof method === 'function') {
method(params)
}
})
} catch (err) {
console.error(err)
}
}
getEditForm (args = {}) {
const node = createNode('div', { class: window.env.querySelectors.modal.slice(1) },
Templates.templates.editForm)
if (args.legend) {
node.querySelector('legend').innerHTML = args.legend
}
this.root.appendChild(node)
node.style.display = 'block'
this.listener([
[node.querySelector(window.env.querySelectors.btnDestroyModal), 'click', destroyNode, node], //Remove modal dialog
[node.querySelector('form'), 'submit', NotesAPI.saveNote],
])
}
}

View File

@@ -11,12 +11,14 @@ export default class NotesAPI{
* @returns {*} * @returns {*}
*/ */
static getNotes (n, archive = false, key = (window.env.localStorageKey || '') ) { static getNotes (n, archive = false, key = (window.env.localStorageKey || '') ) {
let notes = JSON.parse(localStorage.getItem(key) || '[]') let notes = JSON.parse(localStorage.getItem(key) || '[]')
if( archive === false ){ if( typeof archive === 'boolean' ){
notes = notes.filter( el => el.archive !== 'on' ) notes = notes.filter( el => ( archive ? el.archive === 'on' : el.archive !== 'on' ) )
} }
notes.sort((a, b) => { notes.sort((a, b) => {
return new Date(a.createdAt) > new Date(b.createdAt) ? -1 : 1 return new Date(a.createdAt) > new Date(b.createdAt) ? -1 : 1
}) })
@@ -32,6 +34,7 @@ export default class NotesAPI{
*/ */
static getNotesSanitized( n = 0, archive = false){ static getNotesSanitized( n = 0, archive = false){
let notes = NotesAPI.getNotes(0, archive) let notes = NotesAPI.getNotes(0, archive)
notes = Validation.allAgainstSchema(notes, window.env.schemas.note ) notes = Validation.allAgainstSchema(notes, window.env.schemas.note )
return n > 0 ? notes.slice(0, n) : notes return n > 0 ? notes.slice(0, n) : notes
} }
@@ -68,11 +71,13 @@ export default class NotesAPI{
notes.push(noteToStore) notes.push(noteToStore)
} }
NotesAPI.saveNotesToStorage(notes) NotesAPI.saveNotesToStorage(notes)
document.querySelector( window.env.querySelectors.noteEditForm ).closest('.modal').remove()
} else { } else {
DOMController.alert( 'Could save the note! ' + isValid.errors.join(', '),0, 2 ) DOMController.alert( 'Could save the note! ' + isValid.errors.join(', '),6666, 2 ) //, document.getElementById('modalMsgBox')
} }
console.log(NotesAPI.getNotes())
} }
/** /**

View File

@@ -1,5 +1,26 @@
body{ body{
background-color: aliceblue; background-color: ghostwhite;
margin-top: 100px;
}
tbody td{
font-size: 14px;
}
#as-navbar{
overflow: hidden; position: fixed; left: 0; top: 0; width: 100%
}
.uk-text-truncate{
cursor: text;
}
#appConsole{
display: flex;
z-index: 2;
position: fixed;
bottom: 2rem;
right: 2rem;
} }
.icon{ .icon{
@@ -12,12 +33,26 @@ body{
cursor: pointer; cursor: pointer;
transition: transform 250ms; transition: transform 250ms;
} }
.icon.grid-control:hover{ .icon.grid-control:hover{
filter: brightness(0.1) sepia(1) hue-rotate(90deg) saturate(1); filter: brightness(0.1) sepia(1) hue-rotate(90deg) saturate(1);
transform: translateY(5%); transform: translateY(5%);
} }
@keyframes fadeInAnimation {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.as-removing{
animation: fadeInAnimation ease 3s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
}
.icon.as-transition:hover{ .icon.as-transition:hover{
transition: ease-in-out 100ms; transition: ease-in-out 100ms;

View File

@@ -1,12 +1,12 @@
import grid from './grid' import grid from './grid'
import editForm from './editForm' import editForm from './editForm'
import gridRow from './gridRow' //import gridRow from '../../.trash/gridRow'
export default { export default {
templates: { templates: {
editForm: editForm, editForm: editForm,
gridRow: gridRow // gridRow: gridRow
}, },
grid: grid grid: grid
} }

View File

@@ -1,6 +1,7 @@
export default `<form id="noteEditForm" class="uk-form-horizontal uk-margin-large modal-content" style="background: ghostwhite"> export default `<form id="noteEditForm" class="uk-form-horizontal uk-margin-large modal-content" style="background: ghostwhite">
<fieldset class="uk-fieldset"> <fieldset class="uk-fieldset">
<legend class="uk-legend">Edit Note</legend> <legend class="uk-legend">Edit Note</legend>
<div id="modalMsgBox"></div>
<div class="uk-margin"> <div class="uk-margin">
<label class="uk-form-label" for="formTitle">Title</label> <label class="uk-form-label" for="formTitle">Title</label>
@@ -9,7 +10,7 @@ export default `<form id="noteEditForm" class="uk-form-horizontal uk-margin-larg
</div> </div>
</div> </div>
<div class="uk-margin" style="background: aquamarine"> <div class="uk-margin">
<label class="uk-form-label uk-float-right" for="formCategory">Category</label> <label class="uk-form-label uk-float-right" for="formCategory">Category</label>
<div class="uk-form-controls"> <div class="uk-form-controls">
<select id="formCategory" name="category" class="uk-select"> <select id="formCategory" name="category" class="uk-select">

View File

@@ -1,8 +1,31 @@
export default `<table id="notesGrid" class="uk-table uk-table-small uk-table-responsive1 uk-table-striped uk-table-hover uk-table-divider"> export default `
<div id="as-navbar" class="uk-background-secondary">
<div class="uk-container">
<div class="uk-float-right uk-margin-top uk-margin-right">
<button id="btnCreateNote" class="uk-button uk-button-primary" data-action="getEditForm">Create Note</button>
</div>
<ul id="asSwitcher" class="uk-subnav uk-subnav-pill uk-margin-small-top uk-margin-small-bottom uk-padding-small">
<li class="nav-tab"><a href="/all">All Notes</a></li>
<li class="nav-tab uk-active"><a href="/">Recent Notes</a></li>
<li class="nav-tab"><a href="/archived">Archived Notes</a></li>
<li class="nav-tab"><a href="/analytics">Analytics</a></li>
</ul>
</div>
</div>
<table id="notesGrid" class="uk-table uk-table-small uk-table-responsive1 uk-table-striped uk-table-hover uk-table-divider">
<col width="150">
<col width="250">
<col width="150">
<col>
<col width="180">
<col width="32">
<col width="32">
<col width="32">
<thead> <thead>
<tr> <tr>
<th>Title</th>
<th>Created</th> <th>Created</th>
<th>Title</th>
<th>Category</th> <th>Category</th>
<th>Content</th> <th>Content</th>
<th>Dates</th> <th>Dates</th>
@@ -13,9 +36,9 @@ export default `<table id="notesGrid" class="uk-table uk-table-small uk-table-re
</thead> </thead>
<tbody> <tbody>
</tbody> </tbody>
<tr> <!--<tr>
<td colspan="8" class="uk-margin uk-text-right"> <td colspan="8" class="uk-margin uk-text-right">
<button id="btnCreateNote" class="uk-button uk-button-primary" data-action="getEditForm">Create Note</button> <button id="btnCreateNote" class="uk-button uk-button-primary" data-action="getEditForm">Create Note</button>
</td> </td>
</tr> </tr>-->
</table>` </table>`

View File

@@ -1,13 +0,0 @@
export default `
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td class="uk-text-right">
<div class="uk-button-group">
<button class="uk-button uk-button-default icon icon-archive"></button>
<button class="uk-button uk-button-default"></button>
</div>
</td>
`

View File

@@ -1,56 +1,103 @@
const os = require('os')
const path = require('path') const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin') const glob = require('glob')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const TerserPlugin = require("terser-webpack-plugin")
let mode = 'development', target = 'web' const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const PurgeCSSPlugin = require('purgecss-webpack-plugin')
const svgToMiniDataURI = require('mini-svg-data-uri')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const CSSMQPackerPlugin = require('css-mqpacker-webpack-plugin')
let mode = 'development', target = 'web', isProd = false
const babelExclude = /(node_modules|bower_components)/
if (process.env.NODE_ENV === 'production') { if (process.env.NODE_ENV === 'production') {
mode = 'development' mode = 'development'
target = 'browserslist' target = 'browserslist'
isProd = true
} }
const isDev = process.env.NODE_ENV !== 'production'
const plugins = [ const plugins = [
new CleanWebpackPlugin(), new CleanWebpackPlugin(),
new MiniCssExtractPlugin(), new MiniCssExtractPlugin(),
new PurgeCSSPlugin({
paths: glob.sync(`${path.join(__dirname, 'src')}/**/*`, { nodir: true }),
//only: ['bundle', 'vendor']
}),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
template: './src/index.html', template: './src/index.html',
inject: 'body' inject: 'body',
}), }),
] ]
console.log( process.env.SOURCE_MAP_ENV ) const stylesLoaders = loader => {
const loaders = [
isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
ident: 'postcss',
plugins: [
require('postcss-preset-env'),
],
},
},
},
]
if (loader) {
loaders.push(loader)
}
return loaders
}
module.exports = { module.exports = {
mode: mode, mode: mode,
target: target, target: target,
module: { module: {
rules: [ rules: [
{
test: /\.(jpe?g|png|gif|svg)$/i,
type: "asset",
},
{ {
test: /\.css$/i, test: /\.css$/i,
use: [ use: stylesLoaders(),
{
loader: MiniCssExtractPlugin.loader,
// This is required for asset imports in CSS, such as url()
options: { publicPath: '' },
},
'css-loader',
///"postcss-loader",
// according to the docs, sass-loader should be at the bottom, which
// loads it first to avoid prefixes in your sourcemaps and other issues.
//"sass-loader",
],
}, },
{ {
test: /\.js$/, test: /\.s[ac]ss$/i,
exclude: /node_modules/, use: stylesLoaders('sass-loader'),
},
{
test: /\.m?js$/i,
exclude: babelExclude,
use: { use: {
loader: 'babel-loader', loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
],
plugins: [
'@babel/plugin-proposal-class-properties',
],
},
},
},
{
test: /\.(jpe?g|webp|png|gif|svg)$/i,
type: 'asset/resource',
},
{
test: /\.(svg)$/i,
type: 'asset/inline',
generator: {
dataUrl: content => {
content = content.toString()
return svgToMiniDataURI(content)
},
}, },
}, },
], ],
@@ -61,20 +108,29 @@ module.exports = {
filename: '[name].[hash].js', filename: '[name].[hash].js',
}, },
plugins: plugins, plugins: plugins,
devtool: mode === 'production' ? 'source-map' : ( process.env.SOURCE_MAP_ENV ? 'source-map' : false ), devtool: mode === 'production' ? 'source-map' : (process.env.SOURCE_MAP_ENV ? 'eval-cheap-module-source-map' : false),
optimization: { optimization: {
minimize: true, minimize: isProd,
splitChunks: {
chunks: 'all',
},
minimizer: [ minimizer: [
new TerserPlugin({ `...`,
extractComments: true, new CssMinimizerPlugin({
terserOptions: { parallel: os.cpus().length,
ecma: 5, minimizerOptions: {
compress: { preset: [
drop_console: mode === 'production', 'default', // for advanced need to run `npm i -D cssnano-preset-advanced`
}, {
discardComments: { removeAll: true },
},
],
}, },
}), }),
new CSSMQPackerPlugin({
test: /\.css$/i,
sort: true,
}),
], ],
}, },
devServer: { devServer: {
@@ -82,10 +138,5 @@ module.exports = {
compress: true, compress: true,
port: 1976, port: 1976,
liveReload: true, liveReload: true,
/*overlay: {
warnings: true,
errors: true,
}*/
}, },
} }