From 8cb252bfc8ce71eedc30caa42a9f29610100f8e8 Mon Sep 17 00:00:00 2001 From: csl <453826887@qq.com> Date: Fri, 22 Feb 2019 09:37:00 +0800 Subject: [PATCH 01/12] =?UTF-8?q?2.22=20opt=EF=BC=9A=E6=9A=82=E6=97=B6?= =?UTF-8?q?=E7=A7=BB=E9=99=A4=E6=A8=A1=E6=9D=BF=E6=96=87=E4=BB=B6=E6=94=B9?= =?UTF-8?q?=E5=8A=A8=E6=97=B6=EF=BC=8C=E5=88=B7=E6=96=B0=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server.js | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/server.js b/server.js index 8bdabe2..b7e37be 100644 --- a/server.js +++ b/server.js @@ -50,26 +50,26 @@ new WebpackDevServer(compiler, options).listen(sysConfig.dev.port, sysConfig.dev } }); -// const viewPath = path.join(__dirname, sysConfig.dev.tplPath); -// rm('-rf', viewPath); -// // // 在源码有更新时,更新模板 -// compiler.plugin('emit', function (compilation, cb) { -// // console.log('compilation.assets = ', compilation.assets); -// for (const filename in compilation.assets) { -// if (filename.endsWith('.html')) { -// let filepath = path.resolve(viewPath, filename); -// let dirname = path.dirname(filepath); -// if (!fs.existsSync(dirname)) { -// mkdir('-p', dirname); -// } -// // console.log('compilation.assets[filename].source() = ', compilation.assets[filename].source()); -// fs.writeFile(filepath, compilation.assets[filename].source(), (err) => { -// if (err) throw err; -// }); -// } -// } -// cb(); -// }); +const viewPath = path.join(__dirname, sysConfig.dev.tplPath); +rm('-rf', viewPath); +// // 在源码有更新时,更新模板 +compiler.plugin('emit', function (compilation, cb) { + // console.log('compilation.assets = ', compilation.assets); + for (const filename in compilation.assets) { + if (filename.endsWith('.html')) { + let filepath = path.resolve(viewPath, filename); + let dirname = path.dirname(filepath); + if (!fs.existsSync(dirname)) { + mkdir('-p', dirname); + } + // console.log('compilation.assets[filename].source() = ', compilation.assets[filename].source()); + fs.writeFile(filepath, compilation.assets[filename].source(), (err) => { + if (err) throw err; + }); + } + } + cb(); +}); // // // 当页面模板有改变时,强制刷新页面 // compiler.plugin('compilation', function (compilation) { From 59d4da17d8ea65b028b65e6b7f15bd8a7717fc07 Mon Sep 17 00:00:00 2001 From: csl <453826887@qq.com> Date: Fri, 22 Feb 2019 17:39:31 +0800 Subject: [PATCH 02/12] =?UTF-8?q?2.22=20opt=EF=BC=9A=E5=B0=9D=E8=AF=95?= =?UTF-8?q?=E4=BB=A5=E6=96=B0=E7=9A=84=E6=96=B9=E5=BC=8F=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E5=BC=80=E5=8F=91=E6=9C=8D=E5=8A=A1=EF=BC=8C=E4=BB=A5=E8=A7=A3?= =?UTF-8?q?=E5=86=B3cpu=E5=8D=A0=E7=94=A8=E5=BC=82=E5=B8=B8=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E3=80=82=20next=EF=BC=9A=E4=BC=98=E5=8C=96webpack?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app_temp.js | 72 ++++++++++++++++++++++-------------------- webpack.dev.config.js | 2 +- webpack.temp.config.js | 3 +- 3 files changed, 41 insertions(+), 36 deletions(-) diff --git a/app_temp.js b/app_temp.js index 43035e7..c63778a 100644 --- a/app_temp.js +++ b/app_temp.js @@ -38,8 +38,18 @@ app.use(cookieParser()); app.use(sysConfig.dev.publicPath, express.static(sysConfig.dev.outPutPath)); // app.use(sysConfig.dev.publicPath, express.static(path.join(__dirname, 'dist/static'))); +app.locals.env = process.env.NODE_ENV || 'dev'; + app.use('/', routes); +// 设置代理 +app.use('/dj_server', proxy({ + // target: 'https://api.douban.com/', + target: 'http://localhost:3999', + pathRewrite: {'^/dj_server': ''}, + changeOrigin: true +})); + //ignore favicon.ico request app.use(function (req, res, next) { if (req.url === '/favicon.ico') { @@ -50,14 +60,6 @@ app.use(function (req, res, next) { } }); -// 设置代理 -app.use('/dj_server', proxy({ - // target: 'https://api.douban.com/', - target: 'http://localhost:3999', - pathRewrite: {'^/dj_server': ''}, - changeOrigin: true -})); - // catch 404 and forward to error handler app.use(function (req, res, next) { const err = new Error('Not Found'); @@ -91,7 +93,7 @@ if (isDev) { } if (isDev) { - const serverPort = 25999; + const serverPort = 24999; const webpack = require('webpack'); const webpackDevMiddleware = require('webpack-dev-middleware'); const webpackHotMiddleware = require('webpack-hot-middleware'); @@ -107,41 +109,43 @@ if (isDev) { } })); app.use(webpackHotMiddleware(compiler)); - + const port = sysConfig.dev.port; + // sysConfig.dev.port // browsersync is a nice choice when modifying only views (with their css & js) - const bs = require('browser-sync').create(); - app.listen(sysConfig.dev.port, function () { + let bs = require('browser-sync').create(); + app.listen(port, function () { bs.init({ open: false, ui: false, notify: false, - proxy: 'localhost:' + sysConfig.dev.port, + proxy: 'localhost:' + port, files: ['./src/views/**'], - serverPort: serverPort + // 当前版本 browser-sync,配置项key值不同 + port: serverPort }); console.log(`App (dev) is going to be running on port ${serverPort} (by browsersync).`); }); - var viewPath = path.join(__dirname, sysConfig.dev.tplPath); - rm('-rf', viewPath); - // 在源码有更新时,更新模板 - compiler.plugin('emit', function (compilation, cb) { - // console.log('compilation.assets = ', compilation.assets); - for (var filename in compilation.assets) { - if (filename.endsWith('.html')) { - let filepath = path.resolve(viewPath, filename); - let dirname = path.dirname(filepath); - if (!fs.existsSync(dirname)) { - mkdir('-p', dirname); - } - // console.log('compilation.assets[filename].source() = ', compilation.assets[filename].source()); - fs.writeFile(filepath, compilation.assets[filename].source(), (err) => { - if (err) throw err; - }); - } - } - cb(); - }); + // var viewPath = path.join(__dirname, sysConfig.dev.tplPath); + // rm('-rf', viewPath); + // // 在源码有更新时,更新模板 + // compiler.plugin('emit', function (compilation, cb) { + // // console.log('compilation.assets = ', compilation.assets); + // for (var filename in compilation.assets) { + // if (filename.endsWith('.html')) { + // let filepath = path.resolve(viewPath, filename); + // let dirname = path.dirname(filepath); + // if (!fs.existsSync(dirname)) { + // mkdir('-p', dirname); + // } + // // console.log('compilation.assets[filename].source() = ', compilation.assets[filename].source()); + // fs.writeFile(filepath, compilation.assets[filename].source(), (err) => { + // if (err) throw err; + // }); + // } + // } + // cb(); + // }); } else { app.use(express.static(path.join(__dirname, 'public'))); app.listen(sysConfig.dev.port, function () { diff --git a/webpack.dev.config.js b/webpack.dev.config.js index aab5a0a..f837722 100644 --- a/webpack.dev.config.js +++ b/webpack.dev.config.js @@ -123,7 +123,7 @@ let webpackConfig = { const pages = Object.keys(getEntry('src/views/**/*.html', 'src/views/')); pages.forEach(function (pathname) { const conf = { - filename: '../' + sysConfig.dev.tplPath + '/' + pathname + '.html', // 生成的html存放路径,相对于path + filename: '../' + sysConfig.dev.tplPath + '/' + pathname + '.html', // 生成的html存放路径,相对于outPutPath template: 'src/views/' + pathname + '.html', // html模板路径 inject: false // js插入的位置,true/'head'/'body'/false /* diff --git a/webpack.temp.config.js b/webpack.temp.config.js index 05a692f..28e4c58 100644 --- a/webpack.temp.config.js +++ b/webpack.temp.config.js @@ -18,12 +18,13 @@ var debug = process.env.NODE_ENV !== 'production'; var entries = getEntry('src/scripts/page/**/*.js', 'src/scripts/page/'); var chunks = Object.keys(entries); +// todo 配置还存在问题 图片和资源无法获取到 var webpackConfig = { entry: entries, output: { // path: join(__dirname, 'dist/static'), path: sysConfig.dev.outPutPath, - publicPath: sysConfig.dev.publicPath + '/', + publicPath: `http://localhost:${sysConfig.dev.port}/${sysConfig.dev.publicPath}/`, filename: 'scripts/[name].js', chunkFilename: 'scripts/[id].chunk.js?[chunkhash]' }, From b8425c170778f6bfd27aef76747ac7f598804667 Mon Sep 17 00:00:00 2001 From: csl <453826887@qq.com> Date: Mon, 25 Feb 2019 17:36:05 +0800 Subject: [PATCH 03/12] =?UTF-8?q?2.25=20opt=EF=BC=9A=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=BC=80=E5=8F=91=E7=8E=AF=E5=A2=83=E5=90=AF=E5=8A=A8cpu?= =?UTF-8?q?=E5=8D=A0=E7=94=A8=E5=BC=82=E5=B8=B8=E9=97=AE=E9=A2=98=E3=80=82?= =?UTF-8?q?=EF=BC=88node=20=E5=AD=90=E8=BF=9B=E7=A8=8B=E5=90=AF=E5=8A=A8ht?= =?UTF-8?q?tp=20server=20=E4=B8=8D=E7=9F=A5=E9=81=93=E4=B8=BA=E4=BB=80?= =?UTF-8?q?=E4=B9=88cpu=E5=B0=B1=E4=BC=9A=E8=A2=AB=E5=A4=A7=E9=87=8F?= =?UTF-8?q?=E5=8D=A0=E7=94=A8=EF=BC=8C=E7=8E=B0=E6=94=B9=E6=88=90=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=91=BD=E4=BB=A4=E5=90=AF=E5=8A=A8=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/www | 2 +- .../webpack.temp.config.js | 84 +++++++++-------- app.js => dev_temp/app.js | 6 +- app_temp.js => dev_temp/app_temp.js | 75 ++++++++------- server.js => dev_temp/server.js | 4 +- dev_temp/server_temp.js | 87 ++++++++++++++++++ expressServer.js | 91 +++++++++++++++++++ package.json | 5 +- readme.md | 3 - sysConfig/index.js | 12 +-- webpack.dev.config.js | 2 +- webpackDevServer.js | 66 ++++++++++++++ 12 files changed, 343 insertions(+), 94 deletions(-) rename webpack.temp.config.js => build/webpack.temp.config.js (72%) rename app.js => dev_temp/app.js (92%) rename app_temp.js => dev_temp/app_temp.js (64%) rename server.js => dev_temp/server.js (96%) create mode 100644 dev_temp/server_temp.js create mode 100644 expressServer.js create mode 100644 webpackDevServer.js diff --git a/bin/www b/bin/www index 7a3af09..7841bb4 100644 --- a/bin/www +++ b/bin/www @@ -4,7 +4,7 @@ * Module dependencies. */ -var app = require('../app'); +var app = require('../dev_temp/app'); var debug = require('debug')('webpack-art-template:server'); var reload = require('reload'); var http = require('http'); diff --git a/webpack.temp.config.js b/build/webpack.temp.config.js similarity index 72% rename from webpack.temp.config.js rename to build/webpack.temp.config.js index 28e4c58..dfbcfa2 100644 --- a/webpack.temp.config.js +++ b/build/webpack.temp.config.js @@ -1,33 +1,32 @@ // 移除node开发环境,webpack警告 process.noDeprecation = true; -var path = require('path'); -var {resolve, join} = path; -var glob = require('glob'); -var webpack = require('webpack'); -var sysConfig = require('./sysConfig'); -var utils = require('./build/utils'); -var HtmlWebpackPlugin = require('html-webpack-plugin'); +const path = require('path'); +const glob = require('glob'); +const webpack = require('webpack'); +const sysConfig = require('../sysConfig/index'); +const utils = require('./utils'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const CleanWebpackPlugin = require('clean-webpack-plugin'); +const CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin; +const UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; +const ExtractTextPlugin = require('extract-text-webpack-plugin'); -var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin; -var UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; -var ExtractTextPlugin = require('extract-text-webpack-plugin'); - -var debug = process.env.NODE_ENV !== 'production'; - -var entries = getEntry('src/scripts/page/**/*.js', 'src/scripts/page/'); -var chunks = Object.keys(entries); - -// todo 配置还存在问题 图片和资源无法获取到 -var webpackConfig = { +const debug = process.env.NODE_ENV !== 'production'; +const relatePath = ''; +const entries = getEntry(relatePath + 'src/scripts/page/**/*.js', relatePath + 'src/scripts/page/'); +const chunks = Object.keys(entries); +const publicPath = debug ? `http://192.168.2.167:${sysConfig.dev.port}/` : `.${sysConfig.dev.publicPath}/`; +let webpackConfig = { entry: entries, output: { - // path: join(__dirname, 'dist/static'), - path: sysConfig.dev.outPutPath, - publicPath: `http://localhost:${sysConfig.dev.port}/${sysConfig.dev.publicPath}/`, + // path: sysConfig.dev.outPutPath, + path: '/', + publicPath: publicPath, filename: 'scripts/[name].js', chunkFilename: 'scripts/[id].chunk.js?[chunkhash]' }, + devtool: 'eval-source-map', module: { rules: [ // ...utils.styleLoaders({sourceMap: sysConfig.dev.cssSourceMap, usePostCSS: true}), @@ -59,23 +58,22 @@ var webpackConfig = { }] }, // js babel编译,团购项目需要支持ie8,所以暂时不用Babel编译 - // { - // test: /\.js$/, - // loader: 'babel-loader', - // //resolve('node_modules/djcpsweb') - // include: [ - // resolve('src'), - // resolve('test'), - // resolve('node_modules/webpack-dev-server/client') - // ] - // }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [ + resolve('src'), + // resolve('test'), + resolve('node_modules/webpack-dev-server/client') + ] + }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader', options: { limit: 10000, - name: utils.assetsPath('img/[name].[hash:7].[ext]'), - publicPath: '../' + name: utils.assetsPath('img/[name].[hash:7].[ext]') + // publicPath: '../' } }, { @@ -102,6 +100,7 @@ var webpackConfig = { ] }, plugins: [ + new CleanWebpackPlugin(['dist']), new webpack.ProvidePlugin({ // 加载jq $: 'jquery' }), @@ -120,7 +119,7 @@ var webpackConfig = { }) ] }; -var hotMiddlewareScript = 'webpack-hot-middleware/client?reload=true'; +const hotMiddlewareScript = 'webpack-hot-middleware/client?reload=true'; //todo webpack/hot/dev-server? for (const key of Object.keys(webpackConfig.entry)) { @@ -131,9 +130,9 @@ webpackConfig.plugins.push( new webpack.NoEmitOnErrorsPlugin() ); -var pages = Object.keys(getEntry('src/views/**/*.html', 'src/views/')); +const pages = Object.keys(getEntry(relatePath + 'src/views/**/*.html', relatePath + 'src/views/')); pages.forEach(function (pathname) { - var conf = { + const conf = { filename: '../' + sysConfig.dev.tplPath + '/' + pathname + '.html', // 生成的html存放路径,相对于path template: 'src/views/' + pathname + '.html', // html模板路径 inject: false // js插入的位置,true/'head'/'body'/false @@ -149,7 +148,7 @@ pages.forEach(function (pathname) { // } }; if (pathname in webpackConfig.entry) { - conf.favicon = path.resolve(__dirname, 'src/imgs/favicon.ico'); + conf.favicon = path.resolve(__dirname, '../favicon.ico'); conf.inject = 'body'; conf.chunks = ['vendors', pathname]; conf.hash = true; @@ -160,10 +159,11 @@ pages.forEach(function (pathname) { module.exports = webpackConfig; function getEntry(globPath, pathDir) { - var files = glob.sync(globPath); - var entries = {}, entry, dirname, basename, pathname, extname; + const files = glob.sync(globPath); + const entries = {}; + let {entry, dirname, basename, pathname, extname} = {}; - for (var i = 0; i < files.length; i++) { + for (let i = 0; i < files.length; i++) { entry = files[i]; dirname = path.dirname(entry); extname = path.extname(entry); @@ -177,3 +177,7 @@ function getEntry(globPath, pathDir) { } return entries; } + +function resolve(dir) { + return path.join(__dirname, '..', dir); +} diff --git a/app.js b/dev_temp/app.js similarity index 92% rename from app.js rename to dev_temp/app.js index 1d64703..fb12d4c 100644 --- a/app.js +++ b/dev_temp/app.js @@ -5,8 +5,8 @@ const logger = require('morgan'); const cookieParser = require('cookie-parser'); const bodyParser = require('body-parser'); const merge = require('webpack-merge'); -const sysConfig = require('./sysConfig'); -const routes = require('./routes/index'); +const sysConfig = require('../sysConfig/index'); +const routes = require('../routes/index'); // 代理插件 const proxy = require('http-proxy-middleware'); @@ -17,7 +17,7 @@ const app = express(); app.use(cors()); // view engine setup -const {artTemplateOption} = require('./lib/art-template.js'); +const {artTemplateOption} = require('../lib/art-template.js'); app.engine('.html', require('express-art-template')); app.set('view options', merge(artTemplateOption, { //todo 配置项确定 diff --git a/app_temp.js b/dev_temp/app_temp.js similarity index 64% rename from app_temp.js rename to dev_temp/app_temp.js index c63778a..fad767b 100644 --- a/app_temp.js +++ b/dev_temp/app_temp.js @@ -1,26 +1,28 @@ const express = require('express'); const fs = require('fs'); -require('shelljs/global'); +// require('shelljs/global'); const path = require('path'); const logger = require('morgan'); const cookieParser = require('cookie-parser'); const bodyParser = require('body-parser'); const merge = require('webpack-merge'); -const sysConfig = require('./sysConfig'); -const routes = require('./routes/index'); +const sysConfig = require('../sysConfig/index'); +const routes = require('../routes/index'); // 代理插件 const proxy = require('http-proxy-middleware'); const cors = require('cors'); const app = express(); -const isDev = app.get('env') === 'development'; +// const isDev = app.get('env') === 'development'; + +const isDev = false; // todo cors将设置access-control-allow-origin:*,解决跨域问题( express proxy) app.use(cors()); // view engine setup -const {artTemplateOption} = require('./lib/art-template.js'); +const {artTemplateOption} = require('../lib/art-template.js'); app.engine('.html', require('express-art-template')); app.set('view options', merge(artTemplateOption, { //todo 配置项确定 @@ -31,14 +33,15 @@ app.set('views', path.join(__dirname, sysConfig.dev.tplPath)); // uncomment after placing your favicon in /public // app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))) -app.use(logger('dev')); +// app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({extended: false})); app.use(cookieParser()); -app.use(sysConfig.dev.publicPath, express.static(sysConfig.dev.outPutPath)); -// app.use(sysConfig.dev.publicPath, express.static(path.join(__dirname, 'dist/static'))); +// app.use(sysConfig.dev.publicPath, express.static(sysConfig.dev.outPutPath)); + +// app.use(sysConfig.dev.publicPath, express.static(path.join(__dirname, 'dist/static'))); //delete -app.locals.env = process.env.NODE_ENV || 'dev'; +// app.locals.env = process.env.NODE_ENV || 'dev'; app.use('/', routes); @@ -97,7 +100,7 @@ if (isDev) { const webpack = require('webpack'); const webpackDevMiddleware = require('webpack-dev-middleware'); const webpackHotMiddleware = require('webpack-hot-middleware'); - const webpackDevConfig = require('./webpack.temp.config.js'); + const webpackDevConfig = require('../build/webpack.temp.config.js'); const compiler = webpack(webpackDevConfig); @@ -119,38 +122,40 @@ if (isDev) { ui: false, notify: false, proxy: 'localhost:' + port, - files: ['./src/views/**'], + // files: ['./src/views/**'], // 当前版本 browser-sync,配置项key值不同 port: serverPort }); console.log(`App (dev) is going to be running on port ${serverPort} (by browsersync).`); }); - // var viewPath = path.join(__dirname, sysConfig.dev.tplPath); - // rm('-rf', viewPath); - // // 在源码有更新时,更新模板 - // compiler.plugin('emit', function (compilation, cb) { - // // console.log('compilation.assets = ', compilation.assets); - // for (var filename in compilation.assets) { - // if (filename.endsWith('.html')) { - // let filepath = path.resolve(viewPath, filename); - // let dirname = path.dirname(filepath); - // if (!fs.existsSync(dirname)) { - // mkdir('-p', dirname); - // } - // // console.log('compilation.assets[filename].source() = ', compilation.assets[filename].source()); - // fs.writeFile(filepath, compilation.assets[filename].source(), (err) => { - // if (err) throw err; - // }); - // } - // } - // cb(); - // }); + var viewPath = path.join(__dirname, sysConfig.dev.tplPath); + rm('-rf', viewPath); + // 在源码有更新时,更新模板 + compiler.plugin('emit', function (compilation, cb) { + // console.log('compilation.assets = ', compilation.assets); + for (var filename in compilation.assets) { + if (filename.endsWith('.html')) { + let filepath = path.resolve(viewPath, filename); + let dirname = path.dirname(filepath); + if (!fs.existsSync(dirname)) { + mkdir('-p', dirname); + } + // console.log('compilation.assets[filename].source() = ', compilation.assets[filename].source()); + fs.writeFile(filepath, compilation.assets[filename].source(), (err) => { + if (err) throw err; + }); + } + } + cb(); + }); } else { - app.use(express.static(path.join(__dirname, 'public'))); - app.listen(sysConfig.dev.port, function () { - console.log(`App (production) is now running on port ${sysConfig.dev.port}!`); + var port = process.env.PORT || sysConfig.dev.expressPort; + app.use(sysConfig.dev.publicPath, express.static(sysConfig.dev.outPutPath)); + // app.use(express.static(path.join(__dirname, 'public'))); + app.listen(port, function () { + console.log(`App (production) is now running on port ${port}!`); }); } -module.exports = app; +// module.exports = app; diff --git a/server.js b/dev_temp/server.js similarity index 96% rename from server.js rename to dev_temp/server.js index b7e37be..64e464f 100644 --- a/server.js +++ b/dev_temp/server.js @@ -2,8 +2,8 @@ const fs = require('fs'); const path = require('path'); const webpack = require('webpack'); const WebpackDevServer = require('webpack-dev-server'); -const config = require('./webpack.dev.config'); -const sysConfig = require('./sysConfig'); +const config = require('../webpack.dev.config'); +const sysConfig = require('../sysConfig/index'); const merge = require('webpack-merge'); require('shelljs/global'); diff --git a/dev_temp/server_temp.js b/dev_temp/server_temp.js new file mode 100644 index 0000000..4830f56 --- /dev/null +++ b/dev_temp/server_temp.js @@ -0,0 +1,87 @@ +const fs = require('fs'); +const path = require('path'); +const webpack = require('webpack'); +const WebpackDevServer = require('webpack-dev-server'); +const config = require('../build/webpack.temp.config'); +const sysConfig = require('../sysConfig/index'); +const merge = require('webpack-merge'); +require('shelljs/global'); + +let expressPort = sysConfig.dev.expressPort || 24999; +let serverPort = sysConfig.dev.port || 2082; +// +// const exec = require('child_process').exec; +// // const cmdStr = `cross-env PORT=${serverPort} supervisor ./bin/www`; +// const cmdStr = `cross-env NODE_ENV=development PORT=${expressPort} supervisor app_temp`; +// +// exec(cmdStr, function (err, stdout, stderr) { +// if (err) { +// console.error('err = ', err); +// } else { +// console.log('stdout = ', stdout); +// } +// }); + +//todo webpack/hot/dev-server? +// for (const i in config.entry) { +// config.entry[i].unshift('webpack-dev-server/client?http://localhost:' + sysConfig.dev.port, 'webpack/hot/dev-server'); +// } +// config.plugins.push(new webpack.HotModuleReplacementPlugin()); + +const options = { + // contentBase: './dist', + // publicPath: sysConfig.dev.publicPath + '/', + publicPath: '/', + hot: true, + port: serverPort, + host: sysConfig.dev.host, + proxy: { + '*': 'http://localhost:' + expressPort + } + // open: sysConfig.dev.autoOpenBrowser +}; + +WebpackDevServer.addDevServerEntrypoints(config, options); + +const compiler = webpack(config); +// 启动服务 +new WebpackDevServer(compiler, options).listen(serverPort, sysConfig.dev.host, function (err) { + if (err) { + console.log(err); + } else { + console.log(`dev server on http://${sysConfig.dev.host}:${serverPort}\n`); + } +}); + +const viewPath = path.join(__dirname, sysConfig.dev.tplPath); +rm('-rf', viewPath); +// // 在源码有更新时,更新模板 +compiler.plugin('emit', function (compilation, cb) { + // console.log('compilation.assets = ', compilation.assets); + for (const filename in compilation.assets) { + if (filename.endsWith('.html')) { + let filepath = path.resolve(viewPath, filename); + let dirname = path.dirname(filepath); + if (!fs.existsSync(dirname)) { + mkdir('-p', dirname); + } + // console.log('compilation.assets[filename].source() = ', compilation.assets[filename].source()); + fs.writeFile(filepath, compilation.assets[filename].source(), (err) => { + if (err) throw err; + }); + } + } + cb(); +}); +// +// // 当页面模板有改变时,强制刷新页面 +// compiler.plugin('compilation', function (compilation) { +// compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { +// // todo 刷新浏览器 +// /** +// * 实际项目中,应该使用webpack-dev-middleware和webpack-hot-middleware中间件, +// * 结合node库express/koa等使用。 +// */ +// cb(); +// }); +// }); diff --git a/expressServer.js b/expressServer.js new file mode 100644 index 0000000..13ebeec --- /dev/null +++ b/expressServer.js @@ -0,0 +1,91 @@ +const express = require('express'); +// const fs = require('fs'); +// require('shelljs/global'); +const path = require('path'); +const logger = require('morgan'); +const cookieParser = require('cookie-parser'); +const bodyParser = require('body-parser'); +const merge = require('webpack-merge'); +const sysConfig = require('./sysConfig'); +const routes = require('./routes/index'); + +// 代理插件 +const proxy = require('http-proxy-middleware'); +const cors = require('cors'); + +const app = express(); +// const isDev = app.get('env') === 'development'; + +//cors将设置access-control-allow-origin:*,解决跨域问题( express proxy) +app.use(cors()); + +// view engine setup +const {artTemplateOption} = require('./lib/art-template.js'); +app.engine('.html', require('express-art-template')); +app.set('view options', merge(artTemplateOption, { + //todo 配置项确定 + extname: '.html' +})); + +app.set('views', path.join(__dirname, sysConfig.dev.tplPath)); +// uncomment after placing your favicon in /public +// app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))) + +app.use(logger('dev')); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({extended: false})); +app.use(cookieParser()); + +// app.locals.env = process.env.NODE_ENV || 'dev'; + +app.use('/', routes); + +// 设置代理 +app.use('/dj_server', proxy({ + // target: 'https://api.douban.com/', + target: 'http://localhost:3999', + pathRewrite: {'^/dj_server': ''}, + changeOrigin: true +})); + +//ignore favicon.ico request +app.use(function (req, res, next) { + res.writeHead(200, { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive' + }); + // if (req.url === '/favicon.ico') { + // console.log('ignore /favicon.ico'); + // } else { + // res.end(); + // } + res.end(); +}); + +// catch 404 and forward to error handler +app.use(function (req, res, next) { + const err = new Error('Not Found'); + err.status = 404; + next(err); +}); + +// error handlers + +// development error handler +// will print stacktrace +app.use(function (err, req, res, next) { + res.status(err.status || 500); + console.log(err); + res.render('error', { + message: err.message, + error: err + }); +}); + +const port = process.env.PORT || sysConfig.dev.expressPort; +app.use(sysConfig.dev.publicPath, express.static(sysConfig.dev.outPutPath)); +// app.use(express.static(path.join(__dirname, 'public'))); +app.listen(port, function () { + console.log(`App (production) is now running on port ${port}!`); +}); diff --git a/package.json b/package.json index 4eb3e95..f9a205d 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,7 @@ "version": "1.0.0", "main": "index.js", "scripts": { - "dev": "node server", - "browsersync": "cross-env NODE_ENV=development node app_temp", + "start": "concurrently \"node expressServer\" \"node webpackDevServer\"", "build:webpack": "cross-env NODE_ENV=production webpack --config webpack.dev.config.js --progress --hide-modules", "node:compile": "node build/compile_node/compile-art-template", "build": "npm run build:webpack&&npm run node:compile" @@ -13,6 +12,7 @@ "author": "", "license": "ISC", "dependencies": { + "@babel/polyfill": "^7.2.5", "art-template": "^4.13.2", "body-parser": "^1.18.3", "cookie-parser": "^1.4.3", @@ -60,6 +60,7 @@ "postcss-uncss": "^0.15.0", "postcss-write-svg": "jonathantneal/postcss-write-svg", "reload": "^2.4.0", + "resolve-url-loader": "^3.0.1", "shelljs": "^0.8.3", "style-loader": "^0.13.0", "url-loader": "^1.1.2", diff --git a/readme.md b/readme.md index 58b6749..e69de29 100644 --- a/readme.md +++ b/readme.md @@ -1,3 +0,0 @@ -最近在尝试用webpack配合art-template-loader在build的时候,编译带参数(例如:{{className}})的html模板,输出html文件。 -在html-webpack-plugin配置这里,不知道怎么进行配置,才能”传入参数(option)“让其进行编译 - diff --git a/sysConfig/index.js b/sysConfig/index.js index 31b8ff8..70f019a 100644 --- a/sysConfig/index.js +++ b/sysConfig/index.js @@ -11,18 +11,16 @@ let path = require('path'); // } module.exports = { dev: { - host: 'localhost', + host: '0.0.0.0', port: 2082, + serverPort: 2082, + expressPort: 24999, autoOpenBrowser: true, // Paths assetsSubDirectory: '', publicPath: '/static', tplPath: 'temp_views', - outPutPath: path.join(__dirname, '../dist/static'), - - proxyTable: { - '/djwmsservice': 'http://192.168.2.167:3000' - } + outPutPath: path.join(__dirname, '../dist/static') }, build: { @@ -31,7 +29,7 @@ module.exports = { // // // Paths // assetsRoot: path.resolve(__dirname, '../dist'), - assetsSubDirectory: '', + assetsSubDirectory: '' // assetsPublicPath: '/' } }; diff --git a/webpack.dev.config.js b/webpack.dev.config.js index f837722..06f7dd0 100644 --- a/webpack.dev.config.js +++ b/webpack.dev.config.js @@ -22,7 +22,7 @@ let webpackConfig = { output: { // path: join(__dirname, 'dist/static'), path: sysConfig.dev.outPutPath, - publicPath: sysConfig.dev.publicPath + '/', + publicPath: `.${sysConfig.dev.publicPath}/`, filename: 'scripts/[name].js', chunkFilename: 'scripts/[id].chunk.js?[chunkhash]' }, diff --git a/webpackDevServer.js b/webpackDevServer.js new file mode 100644 index 0000000..22a89bb --- /dev/null +++ b/webpackDevServer.js @@ -0,0 +1,66 @@ +const fs = require('fs'); +const path = require('path'); +const webpack = require('webpack'); +const WebpackDevServer = require('webpack-dev-server'); +const config = require('./build/webpack.temp.config'); +const sysConfig = require('./sysConfig'); +require('shelljs/global'); + +let expressPort = sysConfig.dev.expressPort || 24999; +let serverPort = sysConfig.dev.serverPort || 2082; + +const options = { + publicPath: '/', + hot: true, + port: serverPort, + host: sysConfig.dev.host, + proxy: { + '*': 'http://localhost:' + expressPort + } + // open: sysConfig.dev.autoOpenBrowser +}; + +WebpackDevServer.addDevServerEntrypoints(config, options); + +const compiler = webpack(config); +// 启动服务 +new WebpackDevServer(compiler, options).listen(serverPort, sysConfig.dev.host, function (err) { + if (err) { + console.log(err); + } else { + console.log(`dev server on http://${sysConfig.dev.host}:${serverPort}\n`); + } +}); + +const viewPath = path.join(__dirname, sysConfig.dev.tplPath); +rm('-rf', viewPath); +// // 在源码有更新时,更新模板 +compiler.plugin('emit', function (compilation, cb) { + // console.log('compilation.assets = ', compilation.assets); + for (const filename in compilation.assets) { + if (filename.endsWith('.html')) { + let filepath = path.resolve(viewPath, filename); + let dirname = path.dirname(filepath); + if (!fs.existsSync(dirname)) { + mkdir('-p', dirname); + } + // console.log('compilation.assets[filename].source() = ', compilation.assets[filename].source()); + fs.writeFile(filepath, compilation.assets[filename].source(), (err) => { + if (err) throw err; + }); + } + } + cb(); +}); +// +// // 当页面模板有改变时,强制刷新页面 +// compiler.plugin('compilation', function (compilation) { +// compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { +// // todo 刷新浏览器 +// /** +// * 实际项目中,应该使用webpack-dev-middleware和webpack-hot-middleware中间件, +// * 结合node库express/koa等使用。 +// */ +// cb(); +// }); +// }); From 6ae621878bbe27e92ce4d25ef7cca6ad49158665 Mon Sep 17 00:00:00 2001 From: csl <453826887@qq.com> Date: Tue, 26 Feb 2019 10:07:03 +0800 Subject: [PATCH 04/12] =?UTF-8?q?2.26=20opt=EF=BC=9A=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E6=89=93=E5=8D=B0=E6=97=A5=E5=BF=97=E6=96=B9?= =?UTF-8?q?=E4=BE=BF=E5=BF=AB=E9=80=9F=E8=AE=BF=E9=97=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- expressServer.js | 2 +- webpackDevServer.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/expressServer.js b/expressServer.js index 13ebeec..2174846 100644 --- a/expressServer.js +++ b/expressServer.js @@ -43,7 +43,7 @@ app.use('/', routes); // 设置代理 app.use('/dj_server', proxy({ // target: 'https://api.douban.com/', - target: 'http://localhost:3999', + target: 'http://192.168.2.167:3999', pathRewrite: {'^/dj_server': ''}, changeOrigin: true })); diff --git a/webpackDevServer.js b/webpackDevServer.js index 22a89bb..3f24d1c 100644 --- a/webpackDevServer.js +++ b/webpackDevServer.js @@ -28,7 +28,7 @@ new WebpackDevServer(compiler, options).listen(serverPort, sysConfig.dev.host, f if (err) { console.log(err); } else { - console.log(`dev server on http://${sysConfig.dev.host}:${serverPort}\n`); + console.log(`dev server on http://localhost:${serverPort}\n`); } }); From aed55433c53f6b706b120a7a42798677a306bb4d Mon Sep 17 00:00:00 2001 From: csl <453826887@qq.com> Date: Thu, 7 Mar 2019 17:52:37 +0800 Subject: [PATCH 05/12] =?UTF-8?q?3.7=20opt=EF=BC=9Abuild=20=E6=95=B4?= =?UTF-8?q?=E7=90=86=EF=BC=8Cwebpack=20ie=208=20=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .babelrc | 34 + .eslintrc.js | 11 - bin/www | 4 +- build/compile_node/compile-art-template.js | 2 +- build/utils.js | 36 +- .../webpack.base.conf.js | 84 +- build/webpack.dev.conf.js | 94 ++ build/webpack.prod.conf.js | 111 ++ build/webpack.temp.config.js | 21 +- dev_temp/app.js | 74 - dev_temp/app_temp.js | 161 -- dev_temp/server.js | 84 - dev_temp/server_temp.js | 87 -- expressServer.js | 3 +- package.json | 31 +- src/static/css/normalize.css | 424 ++++++ src/static/js/es5-sham_v2.2.0.js | 446 ++++++ src/static/js/es5_shim_v2.2.0.js | 1346 +++++++++++++++++ src/styles/common/main.css | 282 ++++ src/styles/page/index.less | 4 +- src/views/common/meta.html | 14 +- sysConfig/index.js | 21 +- webpackDevServer.js | 5 +- 23 files changed, 2847 insertions(+), 532 deletions(-) create mode 100644 .babelrc rename webpack.dev.config.js => build/webpack.base.conf.js (51%) create mode 100644 build/webpack.dev.conf.js create mode 100644 build/webpack.prod.conf.js delete mode 100644 dev_temp/app.js delete mode 100644 dev_temp/app_temp.js delete mode 100644 dev_temp/server.js delete mode 100644 dev_temp/server_temp.js create mode 100644 src/static/css/normalize.css create mode 100644 src/static/js/es5-sham_v2.2.0.js create mode 100644 src/static/js/es5_shim_v2.2.0.js create mode 100644 src/styles/common/main.css diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..632e90e --- /dev/null +++ b/.babelrc @@ -0,0 +1,34 @@ +{ + "presets": [ + [ + "env", + { + "targets": { + "browsers": [ + "ie>=8", + "chrome>=49" + ] + }, + "loose": true, + "modules": false, + "debug": false, + "uglify": false, + "useBuiltIns": true + } + ], + "es2015-loose", + "stage-0" + ], + "plugins": [ + [ + "transform-runtime", + { + "corejs": false, + "helpers": false, + "polyfill": false, + "regenerator": true +// "moduleName": "babel-runtime" + } + ] + ] +} diff --git a/.eslintrc.js b/.eslintrc.js index 816ba7b..29ed68f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -2,18 +2,9 @@ // const rules = require('./eslint/rules'); module.exports = { root: true, - parser: "babel-eslint", - parserOptions: { - ecmaVersion: 6, - sourceType: 'module', - }, env: { browser: true, - // mocha: true, - es6: true, node: true, - amd: true, - // jasmine: false }, globals: { describe: false, @@ -28,10 +19,8 @@ module.exports = { 'eslint:recommended', 'djcps' ], - // required to lint *.vue files plugins: [ "html", - "import", "json", "node", "promise" diff --git a/bin/www b/bin/www index 7841bb4..cbc3e29 100644 --- a/bin/www +++ b/bin/www @@ -6,7 +6,7 @@ var app = require('../dev_temp/app'); var debug = require('debug')('webpack-art-template:server'); -var reload = require('reload'); +// var reload = require('reload'); var http = require('http'); /** @@ -21,7 +21,7 @@ app.set('port', port); */ var server = http.createServer(app); -reload(server, app); +// reload(server, app); /** * Listen on provided port, on all network interfaces. */ diff --git a/build/compile_node/compile-art-template.js b/build/compile_node/compile-art-template.js index bb3524d..34346e8 100644 --- a/build/compile_node/compile-art-template.js +++ b/build/compile_node/compile-art-template.js @@ -30,7 +30,7 @@ let renderData = { }; //html模板所在页面 -const tempaltePath = 'dist/' + sysConfig.dev.tplPath + '/'; +const tempaltePath = sysConfig.dev.tplPath + '/'; const outPutPath = 'dist/'; // rm('-rf', path.join(rootPath, outPutPath)); /** diff --git a/build/utils.js b/build/utils.js index 10aa763..353883b 100644 --- a/build/utils.js +++ b/build/utils.js @@ -2,6 +2,7 @@ const path = require('path'); const config = require('../sysConfig'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); +const glob = require('glob'); // const packageConfig = require('../package.json'); /** @@ -50,10 +51,10 @@ exports.cssLoaders = function (options) { // Extract CSS when that option is specified // (which is the case during production build) // if (options.extract) { - return ExtractTextPlugin.extract({ - use: loaders - // fallback: 'vue-style-loader' - }); + return ExtractTextPlugin.extract({ + use: loaders + // fallback: 'vue-style-loader' + }); // } else { // return ['vue-style-loader'].concat(loaders); // } @@ -63,7 +64,7 @@ exports.cssLoaders = function (options) { return { css: generateLoaders(), // postcss: generateLoaders(), - less: generateLoaders('less'), + less: generateLoaders('less') // sass: generateLoaders('sass', {indentedSyntax: true}), // scss: generateLoaders('sass'), // stylus: generateLoaders('stylus'), @@ -86,3 +87,28 @@ exports.styleLoaders = function (options) { return output; }; + +/** + * 遍历项目文件,获取入口js,用于生成多页入口 + * @param globPath + * @param pathDir + */ +exports.getEntry = function (globPath, pathDir) { + const files = glob.sync(globPath); + const entries = {}; + let {entry, dirname, basename, pathname, extname} = {}; + + for (let i = 0; i < files.length; i++) { + entry = files[i]; + dirname = path.dirname(entry); + extname = path.extname(entry); + basename = path.basename(entry, extname); + pathname = path.normalize(path.join(dirname, basename)); + pathDir = path.normalize(pathDir); + if (pathname.startsWith(pathDir)) { + pathname = pathname.substring(pathDir.length); + } + entries[pathname] = ['./' + entry]; + } + return entries; +}; diff --git a/webpack.dev.config.js b/build/webpack.base.conf.js similarity index 51% rename from webpack.dev.config.js rename to build/webpack.base.conf.js index 06f7dd0..84ae846 100644 --- a/webpack.dev.config.js +++ b/build/webpack.base.conf.js @@ -1,30 +1,27 @@ // 移除node开发环境,webpack警告 process.noDeprecation = true; -const path = require('path'); -const glob = require('glob'); +// const path = require('path'); const webpack = require('webpack'); -const sysConfig = require('./sysConfig'); -const utils = require('./build/utils'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); +const sysConfig = require('../sysConfig'); +const utils = require('./utils'); + const CleanWebpackPlugin = require('clean-webpack-plugin'); const CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin; -const UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; +// const UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; const ExtractTextPlugin = require('extract-text-webpack-plugin'); -const debug = process.env.NODE_ENV !== 'production'; - -const entries = getEntry('src/scripts/page/**/*.js', 'src/scripts/page/'); +const entries = utils.getEntry('src/scripts/page/**/*.js', 'src/scripts/page/'); const chunks = Object.keys(entries); let webpackConfig = { entry: entries, output: { - // path: join(__dirname, 'dist/static'), - path: sysConfig.dev.outPutPath, - publicPath: `.${sysConfig.dev.publicPath}/`, - filename: 'scripts/[name].js', - chunkFilename: 'scripts/[id].chunk.js?[chunkhash]' + path: sysConfig.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? sysConfig.build.assetsPublicPath + : sysConfig.dev.assetsPublicPath }, module: { rules: [ @@ -72,8 +69,7 @@ let webpackConfig = { loader: 'url-loader', options: { limit: 10000, - name: utils.assetsPath('img/[name].[hash:7].[ext]'), - publicPath: '../' + name: utils.assetsPath('img/[name].[hash:7].[ext]') } }, { @@ -109,61 +105,9 @@ let webpackConfig = { chunks: chunks, minChunks: chunks.length // 提取所有entry共同依赖的模块 }), - new ExtractTextPlugin('styles/[name].css'), // 单独使用link标签加载css并设置路径,相对于output配置中的publickPath - debug ? function () { - } : new UglifyJsPlugin({ // 压缩代码 - compress: { - warnings: false - }, - except: ['$super', '$', 'exports', 'require'] // 排除关键字 - }) + new ExtractTextPlugin('styles/[name].css') // 单独使用link标签加载css并设置路径,相对于output配置中的publickPath + ] }; -const pages = Object.keys(getEntry('src/views/**/*.html', 'src/views/')); -pages.forEach(function (pathname) { - const conf = { - filename: '../' + sysConfig.dev.tplPath + '/' + pathname + '.html', // 生成的html存放路径,相对于outPutPath - template: 'src/views/' + pathname + '.html', // html模板路径 - inject: false // js插入的位置,true/'head'/'body'/false - /* - * 压缩这块,调用了html-minify,会导致压缩时候的很多html语法检查问题, - * 如在html标签属性上使用{{...}}表达式,很多情况下并不需要在此配置压缩项, - * 另外,UglifyJsPlugin会在压缩代码的时候连同html一起压缩。 - * 为避免压缩html,需要在html-loader上配置'html?-minimize',见loaders中html-loader的配置。 - */ - // minify: { //压缩HTML文件 - // removeComments: true, //移除HTML中的注释 - // collapseWhitespace: false //删除空白符与换行符 - // } - }; - if (pathname in webpackConfig.entry) { - conf.favicon = path.resolve(__dirname, 'src/imgs/favicon.ico'); - conf.inject = 'body'; - conf.chunks = ['vendors', pathname]; - conf.hash = true; - } - webpackConfig.plugins.push(new HtmlWebpackPlugin(conf)); -}); - module.exports = webpackConfig; - -function getEntry(globPath, pathDir) { - const files = glob.sync(globPath); - const entries = {}; - let {entry, dirname, basename, pathname, extname} = {}; - - for (let i = 0; i < files.length; i++) { - entry = files[i]; - dirname = path.dirname(entry); - extname = path.extname(entry); - basename = path.basename(entry, extname); - pathname = path.normalize(path.join(dirname, basename)); - pathDir = path.normalize(pathDir); - if (pathname.startsWith(pathDir)) { - pathname = pathname.substring(pathDir.length); - } - entries[pathname] = ['./' + entry]; - } - return entries; -} diff --git a/build/webpack.dev.conf.js b/build/webpack.dev.conf.js new file mode 100644 index 0000000..9c5e483 --- /dev/null +++ b/build/webpack.dev.conf.js @@ -0,0 +1,94 @@ +// 移除node开发环境,webpack警告 +process.noDeprecation = true; + +const path = require('path'); +const webpack = require('webpack'); +const sysConfig = require('../sysConfig/index'); +const utils = require('./utils'); +const merge = require('webpack-merge'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const baseWebpackConfig = require('./webpack.base.conf'); + +const publicPath = `http://192.168.2.167:${sysConfig.dev.port}/`; + +function resolve(dir) { + return path.join(__dirname, '..', dir); +} + +// todo +let webpackConfig = merge(baseWebpackConfig, { + // output: { + // // path: sysConfig.dev.outPutPath, + // path: '/', + // publicPath: '/', + // filename: 'scripts/[name].js', + // chunkFilename: 'scripts/[id].chunk.js?[chunkhash]' + // }, + devtool: 'cheap-module-eval-source-map', + module: { + rules: [ + // js babel编译,团购项目需要支持ie8,所以暂时不用Babel编译 + { + test: /\.js$/, + loader: 'babel-loader', + include: [ + resolve('src'), + // resolve('test'), + resolve('node_modules/webpack-hot-middleware'), + resolve('node_modules/webpack-dev-server/client') + ], + query: { + //处理IE8中Object.defineProperty报错的问题 + presets: ['es2015-loose'] + } + } + ] + }, + plugins: [ + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../src/static'), + to: sysConfig.dev.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}); +// const hotMiddlewareScript = 'webpack-hot-middleware/client?reload=true'; +const hotMiddlewareScript = 'webpack-dev-server/client?http://localhost:' + sysConfig.dev.serverPort; +for (const key of Object.keys(webpackConfig.entry)) { + webpackConfig.entry[key].unshift("babel-polyfill", hotMiddlewareScript, 'webpack/hot/dev-server'); +} +webpackConfig.plugins.push( + new webpack.HotModuleReplacementPlugin(), + new webpack.NoEmitOnErrorsPlugin() +); + +const pages = Object.keys(utils.getEntry('src/views/**/*.html', 'src/views/')); +pages.forEach(function (pathname) { + const conf = { + filename: '../' + sysConfig.dev.tplPath + '/' + pathname + '.html', // 生成的html存放路径,相对于outPutPath + template: 'src/views/' + pathname + '.html', // html模板路径 + inject: false // js插入的位置,true/'head'/'body'/false + /* + * 压缩这块,调用了html-minify,会导致压缩时候的很多html语法检查问题, + * 如在html标签属性上使用{{...}}表达式,很多情况下并不需要在此配置压缩项, + * 另外,UglifyJsPlugin会在压缩代码的时候连同html一起压缩。 + * 为避免压缩html,需要在html-loader上配置'html?-minimize',见loaders中html-loader的配置。 + */ + // minify: { //压缩HTML文件 + // removeComments: true, //移除HTML中的注释 + // collapseWhitespace: false //删除空白符与换行符 + // } + }; + if (pathname in webpackConfig.entry) { + conf.favicon = path.resolve(__dirname, '../src/imgs/favicon.ico'); + conf.inject = 'body'; + conf.chunks = ['vendors', pathname]; + conf.hash = true; + } + webpackConfig.plugins.push(new HtmlWebpackPlugin(conf)); +}); + +module.exports = webpackConfig; diff --git a/build/webpack.prod.conf.js b/build/webpack.prod.conf.js new file mode 100644 index 0000000..ad94407 --- /dev/null +++ b/build/webpack.prod.conf.js @@ -0,0 +1,111 @@ +// 移除node开发环境,webpack警告 +process.noDeprecation = true; + +const path = require('path'); + +const webpack = require('webpack'); +const sysConfig = require('../sysConfig'); +const merge = require('webpack-merge'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const utils = require('./utils'); +const UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; + +const baseWebpackConfig = require('./webpack.base.conf'); + +function resolve(dir) { + return path.join(__dirname, '..', dir); +} + +let webpackConfig = merge(baseWebpackConfig, { + output: { + // // path: join(__dirname, 'dist/static'), + // path: sysConfig.build.outPutPath, + // publicPath: `.${sysConfig.build.publicPath}/`, + // filename: 'scripts/[name].js', + // chunkFilename: 'scripts/[id].chunk.js?[chunkhash]' + path: sysConfig.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + devtool: sysConfig.build.productionSourceMap ? '#source-map' : false, + module: { + rules: [ + // js babel编译,团购项目需要支持ie8,所以暂时不用Babel编译 + { + test: /\.js$/, + use: [ + { + loader: 'babel-loader' + }, + { + loader: 'es3ify-loader' + } + ], + //resolve('node_modules/djcpsweb') + // include: [ + // resolve('src'), + // resolve('node_modules/webpack-build-server/client') + // ] + } + ] + }, + plugins: [ + new UglifyJsPlugin({ // 压缩代码 + output: { + beautify: true,//有正常的空格和断句,注释也会保留, + comments: true, + keep_quoted_props: true + }, + screw_ie8: false, + compress: { + warnings: false, properties: false + }, + except: ['$super', '$', 'exports', 'require'] // 排除关键字 + }), + // new UglifyJSPlugin({ + // compress: {screw_ie8: false}, + // output: {screw_ie8: false}, + // mangle: { + // screw_ie8: false, + // except: ['$'] + // }, + // support_ie8: true + // }) + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../src/static'), + to: sysConfig.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}); + +const pages = Object.keys(utils.getEntry('src/views/**/*.html', 'src/views/')); +pages.forEach(function (pathname) { + const conf = { + filename: '../' + sysConfig.build.tplPath + '/' + pathname + '.html', // 生成的html存放路径,相对于outPutPath + template: 'src/views/' + pathname + '.html', // html模板路径 + inject: false // js插入的位置,true/'head'/'body'/false + /* + * 压缩这块,调用了html-minify,会导致压缩时候的很多html语法检查问题, + * 如在html标签属性上使用{{...}}表达式,很多情况下并不需要在此配置压缩项, + * 另外,UglifyJsPlugin会在压缩代码的时候连同html一起压缩。 + * 为避免压缩html,需要在html-loader上配置'html?-minimize',见loaders中html-loader的配置。 + */ + // minify: { //压缩HTML文件 + // removeComments: true, //移除HTML中的注释 + // collapseWhitespace: false //删除空白符与换行符 + // } + }; + if (pathname in webpackConfig.entry) { + conf.favicon = path.resolve(__dirname, '../src/imgs/favicon.ico'); + conf.inject = 'body'; + conf.chunks = ['vendors', pathname]; + conf.hash = true; + } + webpackConfig.plugins.push(new HtmlWebpackPlugin(conf)); +}); + +module.exports = webpackConfig; diff --git a/build/webpack.temp.config.js b/build/webpack.temp.config.js index dfbcfa2..5732848 100644 --- a/build/webpack.temp.config.js +++ b/build/webpack.temp.config.js @@ -6,6 +6,7 @@ const glob = require('glob'); const webpack = require('webpack'); const sysConfig = require('../sysConfig/index'); const utils = require('./utils'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin; @@ -64,8 +65,13 @@ let webpackConfig = { include: [ resolve('src'), // resolve('test'), + resolve('node_modules/webpack-hot-middleware'), resolve('node_modules/webpack-dev-server/client') - ] + ], + query: { + //处理IE8中Object.defineProperty报错的问题 + presets: ['es2015-loose'] + } }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, @@ -116,14 +122,21 @@ let webpackConfig = { warnings: false }, except: ['$super', '$', 'exports', 'require'] // 排除关键字 - }) + }), + // todo 映射static + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../src/static'), + to: sysConfig.dev.assetsSubDirectory, + ignore: ['.*'] + } + ]) ] }; const hotMiddlewareScript = 'webpack-hot-middleware/client?reload=true'; -//todo webpack/hot/dev-server? for (const key of Object.keys(webpackConfig.entry)) { - webpackConfig.entry[key].unshift(hotMiddlewareScript); + webpackConfig.entry[key].unshift("babel-polyfill", hotMiddlewareScript); } webpackConfig.plugins.push( new webpack.HotModuleReplacementPlugin(), diff --git a/dev_temp/app.js b/dev_temp/app.js deleted file mode 100644 index fb12d4c..0000000 --- a/dev_temp/app.js +++ /dev/null @@ -1,74 +0,0 @@ -const createError = require('http-errors'); -const express = require('express'); -const path = require('path'); -const logger = require('morgan'); -const cookieParser = require('cookie-parser'); -const bodyParser = require('body-parser'); -const merge = require('webpack-merge'); -const sysConfig = require('../sysConfig/index'); -const routes = require('../routes/index'); - -// 代理插件 -const proxy = require('http-proxy-middleware'); -const cors = require('cors'); - -const app = express(); -// todo cors将设置access-control-allow-origin:*,解决跨域问题( express proxy) -app.use(cors()); - -// view engine setup -const {artTemplateOption} = require('../lib/art-template.js'); -app.engine('.html', require('express-art-template')); -app.set('view options', merge(artTemplateOption, { - //todo 配置项确定 - extname: '.html' -})); - -app.set('views', path.join(__dirname, sysConfig.dev.tplPath)); -// uncomment after placing your favicon in /public -// app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))) - -app.use(logger('dev')); -app.use(bodyParser.json()); -app.use(bodyParser.urlencoded({extended: false})); -app.use(cookieParser()); -app.use(sysConfig.dev.publicPath, express.static(sysConfig.dev.outPutPath)); -// app.use(sysConfig.dev.publicPath, express.static(path.join(__dirname, 'dist/static'))); - -app.use('/', routes); - -// 设置代理 attention:set in the top -app.use('/dj_server', proxy({ - // target: 'https://api.douban.com/', - target: 'http://localhost:3999', - pathRewrite: {'^/dj_server': ''}, - changeOrigin: true -})); - -//ignore favicon.ico request -app.use(function (req, res, next) { - if (req.url === '/favicon.ico') { - console.log('ignore'); - } else { - console.log(req.url); - res.end(); - } -}); - -// catch 404 and forward to error handler -app.use(function (req, res, next) { - next(createError(404)); -}); - -// error handler -app.use(function (err, req, res, next) { - // set locals, only providing error in development - res.locals.message = err.message; - res.locals.error = req.app.get('env') === 'development' ? err : {}; - - // render the error page - res.status(err.status || 500); - res.render('error'); -}); - -module.exports = app; diff --git a/dev_temp/app_temp.js b/dev_temp/app_temp.js deleted file mode 100644 index fad767b..0000000 --- a/dev_temp/app_temp.js +++ /dev/null @@ -1,161 +0,0 @@ -const express = require('express'); -const fs = require('fs'); -// require('shelljs/global'); -const path = require('path'); -const logger = require('morgan'); -const cookieParser = require('cookie-parser'); -const bodyParser = require('body-parser'); -const merge = require('webpack-merge'); -const sysConfig = require('../sysConfig/index'); -const routes = require('../routes/index'); - -// 代理插件 -const proxy = require('http-proxy-middleware'); -const cors = require('cors'); - -const app = express(); -// const isDev = app.get('env') === 'development'; - -const isDev = false; - -// todo cors将设置access-control-allow-origin:*,解决跨域问题( express proxy) -app.use(cors()); - -// view engine setup -const {artTemplateOption} = require('../lib/art-template.js'); -app.engine('.html', require('express-art-template')); -app.set('view options', merge(artTemplateOption, { - //todo 配置项确定 - extname: '.html' -})); - -app.set('views', path.join(__dirname, sysConfig.dev.tplPath)); -// uncomment after placing your favicon in /public -// app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))) - -// app.use(logger('dev')); -app.use(bodyParser.json()); -app.use(bodyParser.urlencoded({extended: false})); -app.use(cookieParser()); -// app.use(sysConfig.dev.publicPath, express.static(sysConfig.dev.outPutPath)); - -// app.use(sysConfig.dev.publicPath, express.static(path.join(__dirname, 'dist/static'))); //delete - -// app.locals.env = process.env.NODE_ENV || 'dev'; - -app.use('/', routes); - -// 设置代理 -app.use('/dj_server', proxy({ - // target: 'https://api.douban.com/', - target: 'http://localhost:3999', - pathRewrite: {'^/dj_server': ''}, - changeOrigin: true -})); - -//ignore favicon.ico request -app.use(function (req, res, next) { - if (req.url === '/favicon.ico') { - console.log('ignore'); - } else { - console.log(req.url); - res.end(); - } -}); - -// catch 404 and forward to error handler -app.use(function (req, res, next) { - const err = new Error('Not Found'); - err.status = 404; - next(err); -}); - -// error handlers - -// development error handler -// will print stacktrace -if (isDev) { - app.use(function (err, req, res, next) { - res.status(err.status || 500); - console.log(err); - res.render('error', { - message: err.message, - error: err - }); - }); -} else { - // production error handler - // no stacktraces leaked to user - app.use(function (err, req, res, next) { - res.status(err.status || 500); - res.render('error', { - message: err.message, - error: {} - }); - }); -} - -if (isDev) { - const serverPort = 24999; - const webpack = require('webpack'); - const webpackDevMiddleware = require('webpack-dev-middleware'); - const webpackHotMiddleware = require('webpack-hot-middleware'); - const webpackDevConfig = require('../build/webpack.temp.config.js'); - - const compiler = webpack(webpackDevConfig); - - app.use(webpackDevMiddleware(compiler, { - publicPath: webpackDevConfig.output.publicPath, - noInfo: true, - stats: { - colors: true - } - })); - app.use(webpackHotMiddleware(compiler)); - const port = sysConfig.dev.port; - // sysConfig.dev.port - // browsersync is a nice choice when modifying only views (with their css & js) - let bs = require('browser-sync').create(); - app.listen(port, function () { - bs.init({ - open: false, - ui: false, - notify: false, - proxy: 'localhost:' + port, - // files: ['./src/views/**'], - // 当前版本 browser-sync,配置项key值不同 - port: serverPort - }); - console.log(`App (dev) is going to be running on port ${serverPort} (by browsersync).`); - }); - - var viewPath = path.join(__dirname, sysConfig.dev.tplPath); - rm('-rf', viewPath); - // 在源码有更新时,更新模板 - compiler.plugin('emit', function (compilation, cb) { - // console.log('compilation.assets = ', compilation.assets); - for (var filename in compilation.assets) { - if (filename.endsWith('.html')) { - let filepath = path.resolve(viewPath, filename); - let dirname = path.dirname(filepath); - if (!fs.existsSync(dirname)) { - mkdir('-p', dirname); - } - // console.log('compilation.assets[filename].source() = ', compilation.assets[filename].source()); - fs.writeFile(filepath, compilation.assets[filename].source(), (err) => { - if (err) throw err; - }); - } - } - cb(); - }); -} else { - var port = process.env.PORT || sysConfig.dev.expressPort; - app.use(sysConfig.dev.publicPath, express.static(sysConfig.dev.outPutPath)); - // app.use(express.static(path.join(__dirname, 'public'))); - app.listen(port, function () { - console.log(`App (production) is now running on port ${port}!`); - }); -} - -// module.exports = app; diff --git a/dev_temp/server.js b/dev_temp/server.js deleted file mode 100644 index 64e464f..0000000 --- a/dev_temp/server.js +++ /dev/null @@ -1,84 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const webpack = require('webpack'); -const WebpackDevServer = require('webpack-dev-server'); -const config = require('../webpack.dev.config'); -const sysConfig = require('../sysConfig/index'); -const merge = require('webpack-merge'); -require('shelljs/global'); - -const serverPort = 24999; - -const exec = require('child_process').exec; -const cmdStr = `cross-env PORT=${serverPort} supervisor ./bin/www`; - -exec(cmdStr, function (err, stdout, stderr) { - if (err) { - console.error('err = ', err); - } else { - console.log('stdout = ', stdout); - } -}); - -//todo webpack/hot/dev-server? -// for (const i in config.entry) { -// config.entry[i].unshift('webpack-dev-server/client?http://localhost:' + sysConfig.dev.port, 'webpack/hot/dev-server'); -// } -config.plugins.push(new webpack.HotModuleReplacementPlugin()); - -const options = { - contentBase: './dist', - publicPath: sysConfig.dev.publicPath + '/', - hot: true, - port: sysConfig.dev.port, - host: sysConfig.dev.host, - proxy: { - '*': 'http://localhost:' + serverPort - } - // open: sysConfig.dev.autoOpenBrowser -}; - -WebpackDevServer.addDevServerEntrypoints(config, options); - -const compiler = webpack(config); -// 启动服务 -new WebpackDevServer(compiler, options).listen(sysConfig.dev.port, sysConfig.dev.host, function (err) { - if (err) { - console.log(err); - } else { - console.log(`dev server on http://${sysConfig.dev.host}:${sysConfig.dev.port}\n`); - } -}); - -const viewPath = path.join(__dirname, sysConfig.dev.tplPath); -rm('-rf', viewPath); -// // 在源码有更新时,更新模板 -compiler.plugin('emit', function (compilation, cb) { - // console.log('compilation.assets = ', compilation.assets); - for (const filename in compilation.assets) { - if (filename.endsWith('.html')) { - let filepath = path.resolve(viewPath, filename); - let dirname = path.dirname(filepath); - if (!fs.existsSync(dirname)) { - mkdir('-p', dirname); - } - // console.log('compilation.assets[filename].source() = ', compilation.assets[filename].source()); - fs.writeFile(filepath, compilation.assets[filename].source(), (err) => { - if (err) throw err; - }); - } - } - cb(); -}); -// -// // 当页面模板有改变时,强制刷新页面 -// compiler.plugin('compilation', function (compilation) { -// compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { -// // todo 刷新浏览器 -// /** -// * 实际项目中,应该使用webpack-dev-middleware和webpack-hot-middleware中间件, -// * 结合node库express/koa等使用。 -// */ -// cb(); -// }); -// }); diff --git a/dev_temp/server_temp.js b/dev_temp/server_temp.js deleted file mode 100644 index 4830f56..0000000 --- a/dev_temp/server_temp.js +++ /dev/null @@ -1,87 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const webpack = require('webpack'); -const WebpackDevServer = require('webpack-dev-server'); -const config = require('../build/webpack.temp.config'); -const sysConfig = require('../sysConfig/index'); -const merge = require('webpack-merge'); -require('shelljs/global'); - -let expressPort = sysConfig.dev.expressPort || 24999; -let serverPort = sysConfig.dev.port || 2082; -// -// const exec = require('child_process').exec; -// // const cmdStr = `cross-env PORT=${serverPort} supervisor ./bin/www`; -// const cmdStr = `cross-env NODE_ENV=development PORT=${expressPort} supervisor app_temp`; -// -// exec(cmdStr, function (err, stdout, stderr) { -// if (err) { -// console.error('err = ', err); -// } else { -// console.log('stdout = ', stdout); -// } -// }); - -//todo webpack/hot/dev-server? -// for (const i in config.entry) { -// config.entry[i].unshift('webpack-dev-server/client?http://localhost:' + sysConfig.dev.port, 'webpack/hot/dev-server'); -// } -// config.plugins.push(new webpack.HotModuleReplacementPlugin()); - -const options = { - // contentBase: './dist', - // publicPath: sysConfig.dev.publicPath + '/', - publicPath: '/', - hot: true, - port: serverPort, - host: sysConfig.dev.host, - proxy: { - '*': 'http://localhost:' + expressPort - } - // open: sysConfig.dev.autoOpenBrowser -}; - -WebpackDevServer.addDevServerEntrypoints(config, options); - -const compiler = webpack(config); -// 启动服务 -new WebpackDevServer(compiler, options).listen(serverPort, sysConfig.dev.host, function (err) { - if (err) { - console.log(err); - } else { - console.log(`dev server on http://${sysConfig.dev.host}:${serverPort}\n`); - } -}); - -const viewPath = path.join(__dirname, sysConfig.dev.tplPath); -rm('-rf', viewPath); -// // 在源码有更新时,更新模板 -compiler.plugin('emit', function (compilation, cb) { - // console.log('compilation.assets = ', compilation.assets); - for (const filename in compilation.assets) { - if (filename.endsWith('.html')) { - let filepath = path.resolve(viewPath, filename); - let dirname = path.dirname(filepath); - if (!fs.existsSync(dirname)) { - mkdir('-p', dirname); - } - // console.log('compilation.assets[filename].source() = ', compilation.assets[filename].source()); - fs.writeFile(filepath, compilation.assets[filename].source(), (err) => { - if (err) throw err; - }); - } - } - cb(); -}); -// -// // 当页面模板有改变时,强制刷新页面 -// compiler.plugin('compilation', function (compilation) { -// compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { -// // todo 刷新浏览器 -// /** -// * 实际项目中,应该使用webpack-dev-middleware和webpack-hot-middleware中间件, -// * 结合node库express/koa等使用。 -// */ -// cb(); -// }); -// }); diff --git a/expressServer.js b/expressServer.js index 2174846..a85a9b4 100644 --- a/expressServer.js +++ b/expressServer.js @@ -23,7 +23,6 @@ app.use(cors()); const {artTemplateOption} = require('./lib/art-template.js'); app.engine('.html', require('express-art-template')); app.set('view options', merge(artTemplateOption, { - //todo 配置项确定 extname: '.html' })); @@ -84,7 +83,7 @@ app.use(function (err, req, res, next) { }); const port = process.env.PORT || sysConfig.dev.expressPort; -app.use(sysConfig.dev.publicPath, express.static(sysConfig.dev.outPutPath)); +app.use(sysConfig.dev.assetsPublicPath, express.static(sysConfig.build.assetsRoot)); // app.use(express.static(path.join(__dirname, 'public'))); app.listen(port, function () { console.log(`App (production) is now running on port ${port}!`); diff --git a/package.json b/package.json index f9a205d..61ce7a1 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "main": "index.js", "scripts": { "start": "concurrently \"node expressServer\" \"node webpackDevServer\"", - "build:webpack": "cross-env NODE_ENV=production webpack --config webpack.dev.config.js --progress --hide-modules", + "build:webpack": "cross-env NODE_ENV=production webpack --config build/webpack.prod.conf.js --progress --hide-modules", "node:compile": "node build/compile_node/compile-art-template", "build": "npm run build:webpack&&npm run node:compile" }, @@ -12,7 +12,6 @@ "author": "", "license": "ISC", "dependencies": { - "@babel/polyfill": "^7.2.5", "art-template": "^4.13.2", "body-parser": "^1.18.3", "cookie-parser": "^1.4.3", @@ -21,28 +20,30 @@ "express": "^4.16.4", "http-errors": "~1.6.2", "http-proxy": "^1.17.0", - "morgan": "^1.9.1" + "morgan": "^1.9.1", + "webpack-hot-middleware-ie8": "^2.13.5" }, "devDependencies": { - "art-template-loader": "^1.4.3", "autoprefixer": "^7.1.2", "autoprefixer-loader": "^3.2.0", - "babel-core": "^6.0.0", - "babel-eslint": "^8.2.1", - "babel-loader": "^7.1.1", - "babel-polyfill": "^6.20.0", - "babel-preset-es2015": "^6.0.0", - "babel-preset-stage-3": "^6.17.0", + "babel-core": "^6.26.3", + "babel-loader": "^7.1.5", + "babel-plugin-transform-runtime": "^6.23.0", + "babel-polyfill": "^6.26.0", + "babel-preset-env": "^1.7.0", + "babel-preset-es2015": "^6.24.1", + "babel-preset-es2015-loose": "^8.0.0", + "babel-preset-stage-0": "^6.24.1", "browser-sync": "^2.26.3", "clean-webpack-plugin": "^1.0.1", + "copy-webpack-plugin": "^4.0.1", "cross-env": "^3.0.0", "css-loader": "^0.23.1", + "es3ify-loader": "^0.2.0", "eslint": "^4.2.0", "eslint-config-djcps": "^0.0.4", - "eslint-friendly-formatter": "^3.0.0", "eslint-loader": "^1.9.0", "eslint-plugin-html": "^4.0.2", - "eslint-plugin-import": "^2.7.0", "eslint-plugin-json": "^1.2.0", "eslint-plugin-node": "^5.2.0", "eslint-plugin-promise": "^3.4.0", @@ -56,15 +57,11 @@ "jquery": "^3.3.1", "less": "^3.9.0", "less-loader": "^4.1.0", - "postcss-loader": "^0.9.1", - "postcss-uncss": "^0.15.0", - "postcss-write-svg": "jonathantneal/postcss-write-svg", - "reload": "^2.4.0", "resolve-url-loader": "^3.0.1", "shelljs": "^0.8.3", "style-loader": "^0.13.0", "url-loader": "^1.1.2", - "webpack": "^3.3.0", + "webpack": "^3.12.0", "webpack-dev-middleware": "^2.0.6", "webpack-dev-server": "^2.11.3", "webpack-hot-middleware": "^2.24.3", diff --git a/src/static/css/normalize.css b/src/static/css/normalize.css new file mode 100644 index 0000000..f09a1a2 --- /dev/null +++ b/src/static/css/normalize.css @@ -0,0 +1,424 @@ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ + +/** + * 1. Set default font family to sans-serif. + * 2. Prevent iOS and IE text size adjust after device orientation change, + * without disabling user zoom. + */ +/*123*/ +html { + font-family: sans-serif; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/** + * Remove default margin. + */ + +body { + margin: 0; +} + +/* HTML5 display definitions + ========================================================================== */ + +/** + * Correct `block` display not defined for any HTML5 element in IE 8/9. + * Correct `block` display not defined for `details` or `summary` in IE 10/11 + * and Firefox. + * Correct `block` display not defined for `main` in IE 11. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} + +/** + * 1. Correct `inline-block` display not defined in IE 8/9. + * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. + */ + +audio, +canvas, +progress, +video { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Prevent modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Address `[hidden]` styling not present in IE 8/9/10. + * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22. + */ + +[hidden], +template { + display: none; +} + +/* Links + ========================================================================== */ + +/** + * Remove the gray background color from active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * Improve readability of focused elements when they are also in an + * active/hover state. + */ + +a:active, +a:hover { + outline: 0; +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Address styling not present in IE 8/9/10/11, Safari, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/** + * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. + */ + +b, +strong { + font-weight: bold; +} + +/** + * Address styling not present in Safari and Chrome. + */ + +dfn { + font-style: italic; +} + +/** + * Address variable `h1` font-size and margin within `section` and `article` + * contexts in Firefox 4+, Safari, and Chrome. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/** + * Address styling not present in IE 8/9. + */ + +mark { + background: #ff0; + color: #000; +} + +/** + * Address inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove border when inside `a` element in IE 8/9/10. + */ + +img { + border: 0; +} + +/** + * Correct overflow not hidden in IE 9/10/11. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Grouping content + ========================================================================== */ + +/** + * Address margin not present in IE 8/9 and Safari. + */ + +figure { + margin: 1em 40px; +} + +/** + * Address differences between Firefox and other browsers. + */ + +hr { + box-sizing: content-box; + height: 0; +} + +/** + * Contain overflow in all browsers. + */ + +pre { + overflow: auto; +} + +/** + * Address odd `em`-unit font size rendering in all browsers. + */ + +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} + +/* Forms + ========================================================================== */ + +/** + * Known limitation: by default, Chrome and Safari on OS X allow very limited + * styling of `select`, unless a `border` property is set. + */ + +/** + * 1. Correct color not being inherited. + * Known issue: affects color of disabled elements. + * 2. Correct font properties not being inherited. + * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. + */ + +button, +input, +optgroup, +select, +textarea { + color: inherit; /* 1 */ + font: inherit; /* 2 */ + margin: 0; /* 3 */ +} + +/** + * Address `overflow` set to `hidden` in IE 8/9/10/11. + */ + +button { + overflow: visible; +} + +/** + * Address inconsistent `text-transform` inheritance for `button` and `select`. + * All other form control elements do not inherit `text-transform` values. + * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. + * Correct `select` style inheritance in Firefox. + */ + +button, +select { + text-transform: none; +} + +/** + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Correct inability to style clickable `input` types in iOS. + * 3. Improve usability and consistency of cursor style between image-type + * `input` and others. + */ + +button, +html input[type="button"], /* 1 */ +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ +} + +/** + * Re-set default cursor for disabled elements. + */ + +button[disabled], +html input[disabled] { + cursor: default; +} + +/** + * Remove inner padding and border in Firefox 4+. + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/** + * Address Firefox 4+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +input { + line-height: normal; +} + +/** + * It's recommended that you don't attempt to style these elements. + * Firefox's implementation doesn't respect box-sizing, padding, or width. + * + * 1. Address box sizing set to `content-box` in IE 8/9/10. + * 2. Remove excess padding in IE 8/9/10. + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Fix the cursor style for Chrome's increment/decrement buttons. For certain + * `font-size` values of the `input`, it causes the cursor style of the + * decrement button to change from `default` to `text`. + */ + +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Address `appearance` set to `searchfield` in Safari and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari and Chrome. + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + box-sizing: content-box; /* 2 */ +} + +/** + * Remove inner padding and search cancel button in Safari and Chrome on OS X. + * Safari (but not Chrome) clips the cancel button when the search input has + * padding (and `textfield` appearance). + */ + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/** + * 1. Correct `color` not being inherited in IE 8/9/10/11. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + */ + +legend { + border: 0; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Remove default vertical scrollbar in IE 8/9/10/11. + */ + +textarea { + overflow: auto; +} + +/** + * Don't inherit the `font-weight` (applied by a rule above). + * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. + */ + +optgroup { + font-weight: bold; +} + +/* Tables + ========================================================================== */ + +/** + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} + +td, +th { + padding: 0; +} diff --git a/src/static/js/es5-sham_v2.2.0.js b/src/static/js/es5-sham_v2.2.0.js new file mode 100644 index 0000000..b50d7c2 --- /dev/null +++ b/src/static/js/es5-sham_v2.2.0.js @@ -0,0 +1,446 @@ +// Copyright 2009-2012 by contributors, MIT License +// vim: ts=4 sts=4 sw=4 expandtab + +//Add semicolon to prevent IIFE from being passed as argument to concated code. +; +// Module systems magic dance +(function (definition) { + // RequireJS + if (typeof define == "function") { + define(definition); + // YUI3 + } else if (typeof YUI == "function") { + YUI.add("es5-sham", definition); + // CommonJS and + +
You are using an outdated browser. Please upgrade your browser to improve your experience.
+ diff --git a/sysConfig/index.js b/sysConfig/index.js index 70f019a..3c8dc3d 100644 --- a/sysConfig/index.js +++ b/sysConfig/index.js @@ -12,15 +12,14 @@ let path = require('path'); module.exports = { dev: { host: '0.0.0.0', - port: 2082, serverPort: 2082, expressPort: 24999, autoOpenBrowser: true, - // Paths - assetsSubDirectory: '', - publicPath: '/static', - tplPath: 'temp_views', - outPutPath: path.join(__dirname, '../dist/static') + assetsPublicPath: '/', // 'https://cdn.xxxxx.com', // 添加路径前缀,后续cdn扩展 + assetsSubDirectory: 'static', //静态资源指向目录 + // publicPath: '/static', + tplPath: 'temp_views' + // outPutPath: path.join(__dirname, '../dist/static') }, build: { @@ -28,8 +27,12 @@ module.exports = { // index: path.resolve(__dirname, '../dist/index.html'), // // // Paths - // assetsRoot: path.resolve(__dirname, '../dist'), - assetsSubDirectory: '' - // assetsPublicPath: '/' + assetsRoot: path.resolve(__dirname, '../dist'), + assetsPublicPath: '/', + assetsSubDirectory: 'static', //静态资源指向目录 + // publicPath: '/static', + // outPutPath: path.join(__dirname, '../dist/static'), + tplPath: 'temp_views', + productionSourceMap: false } }; diff --git a/webpackDevServer.js b/webpackDevServer.js index 3f24d1c..bd117e9 100644 --- a/webpackDevServer.js +++ b/webpackDevServer.js @@ -2,7 +2,7 @@ const fs = require('fs'); const path = require('path'); const webpack = require('webpack'); const WebpackDevServer = require('webpack-dev-server'); -const config = require('./build/webpack.temp.config'); +const config = require('./build/webpack.dev.conf'); const sysConfig = require('./sysConfig'); require('shelljs/global'); @@ -11,7 +11,8 @@ let serverPort = sysConfig.dev.serverPort || 2082; const options = { publicPath: '/', - hot: true, + hot: false, + inline: false, port: serverPort, host: sysConfig.dev.host, proxy: { From f7ac703ce81883eee10975581e2c9a72c4bce93a Mon Sep 17 00:00:00 2001 From: csl <453826887@qq.com> Date: Fri, 8 Mar 2019 18:50:00 +0800 Subject: [PATCH 06/12] =?UTF-8?q?3.8=20opt=EF=BC=9A=E5=AE=9A=E4=BD=8Die=20?= =?UTF-8?q?8=20=E5=85=BC=E5=AE=B9=E9=97=AE=E9=A2=98=E7=82=B9=EF=BC=8Crequi?= =?UTF-8?q?re=20=E5=BC=82=E6=AD=A5=E5=8A=A0=E8=BD=BDjs=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E5=9C=A8ie=208=20=E4=B8=AD=E7=BC=96=E8=BE=91=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E5=87=BA=E9=94=99=EF=BC=8Cie9=20=E4=B8=AD=20=E5=8A=A0=E5=85=A5?= =?UTF-8?q?es6-promise=E5=90=8E=E8=83=BD=E5=A4=9F=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .babelrc | 2 +- build/webpack.base.conf.js | 40 ++++++- build/webpack.dev.conf.js | 92 +++++++++++----- build/webpack.prod.conf.js | 82 ++++++++++----- build/webpack.temp.config.js | 196 ----------------------------------- package.json | 5 +- src/scripts/page/index.js | 8 +- src/styles/page/index.less | 2 +- src/views/common/meta.html | 7 +- sysConfig/index.js | 6 +- webpackDevServer.js | 4 +- 11 files changed, 185 insertions(+), 259 deletions(-) delete mode 100644 build/webpack.temp.config.js diff --git a/.babelrc b/.babelrc index 632e90e..34ddd78 100644 --- a/.babelrc +++ b/.babelrc @@ -27,7 +27,7 @@ "helpers": false, "polyfill": false, "regenerator": true -// "moduleName": "babel-runtime" + // "moduleName": "babel-runtime" } ] ] diff --git a/build/webpack.base.conf.js b/build/webpack.base.conf.js index 84ae846..2e9fa5b 100644 --- a/build/webpack.base.conf.js +++ b/build/webpack.base.conf.js @@ -1,7 +1,7 @@ // 移除node开发环境,webpack警告 process.noDeprecation = true; -// const path = require('path'); +const path = require('path'); const webpack = require('webpack'); const sysConfig = require('../sysConfig'); const utils = require('./utils'); @@ -10,10 +10,15 @@ const CleanWebpackPlugin = require('clean-webpack-plugin'); const CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin; // const UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; const ExtractTextPlugin = require('extract-text-webpack-plugin'); +const Es3ifyPlugin = require('es3ify-webpack-plugin'); const entries = utils.getEntry('src/scripts/page/**/*.js', 'src/scripts/page/'); const chunks = Object.keys(entries); +function resolve(dir) { + return path.join(__dirname, '..', dir); +} + let webpackConfig = { entry: entries, output: { @@ -25,6 +30,38 @@ let webpackConfig = { }, module: { rules: [ + // { + // test: /\.js$/, + // loader: 'babel-loader', + // // include: [ + // // resolve('src'), + // // // resolve('test'), + // // // resolve('node_modules/webpack-hot-middleware'), + // // resolve('node_modules/webpack-dev-server/client') + // // ], + // exclude: /(node_modules)/, + // query: { + // //处理IE8中Object.defineProperty报错的问题 + // presets: ['es2015-loose'] + // } + // }, + { + test: /\.(js|jsx|ejs|tpl)$/, + exclude: /(node_modules)/, + //include: path.join(projectDirname, 'src'), + //loaders: ['babel?optional=runtime'] + use: { + loader: 'babel-loader', + /*options: { + presets: ['env'] + }*/ + options: { + presets: ['env', 'es2015-loose'] + //presets: ['env'], + //plugins: ['transform-runtime', 'proxy'] + } + } + }, // ...utils.styleLoaders({sourceMap: sysConfig.dev.cssSourceMap, usePostCSS: true}), { test: /\.css$/, @@ -96,6 +133,7 @@ let webpackConfig = { ] }, plugins: [ + new Es3ifyPlugin(), new CleanWebpackPlugin(['dist']), new webpack.ProvidePlugin({ // 加载jq $: 'jquery' diff --git a/build/webpack.dev.conf.js b/build/webpack.dev.conf.js index 9c5e483..669a10d 100644 --- a/build/webpack.dev.conf.js +++ b/build/webpack.dev.conf.js @@ -10,8 +10,6 @@ const CopyWebpackPlugin = require('copy-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const baseWebpackConfig = require('./webpack.base.conf'); -const publicPath = `http://192.168.2.167:${sysConfig.dev.port}/`; - function resolve(dir) { return path.join(__dirname, '..', dir); } @@ -29,23 +27,52 @@ let webpackConfig = merge(baseWebpackConfig, { module: { rules: [ // js babel编译,团购项目需要支持ie8,所以暂时不用Babel编译 - { - test: /\.js$/, - loader: 'babel-loader', - include: [ - resolve('src'), - // resolve('test'), - resolve('node_modules/webpack-hot-middleware'), - resolve('node_modules/webpack-dev-server/client') - ], - query: { - //处理IE8中Object.defineProperty报错的问题 - presets: ['es2015-loose'] - } - } + // { + // test: /\.js$/, + // loader: 'babel-loader', + // include: [ + // resolve('src'), + // // resolve('test'), + // // resolve('node_modules/webpack-hot-middleware'), + // resolve('node_modules/webpack-dev-server/client') + // ], + // query: { + // //处理IE8中Object.defineProperty报错的问题 + // presets: ['es2015-loose'] + // } + // }, + // { + // test: /\.js$/, + // enforce: "post", + // include: [ + // resolve('src'), + // // resolve('test'), + // // resolve('node_modules/webpack-hot-middleware'), + // resolve('node_modules/webpack-dev-server/client') + // ], + // loader: "es3ify-loader" + // } ] }, plugins: [ + // new webpack.optimize.UglifyJsPlugin({ // 压缩代码 + // output: { + // screw_ie8: false, + // beautify: true, //有正常的空格和断句,注释也会保留, + // comments: true, + // quote_keys: true, + // keep_quoted_props: true + // }, + // screw_ie8: false, + // compress: { + // warnings: false, properties: false, screw_ie8: false + // }, + // mangle: { + // eval: true, + // screw_ie8: false, + // except: ['$super', '$', 'exports', 'require'] // 排除关键字 + // } + // }), new CopyWebpackPlugin([ { from: path.resolve(__dirname, '../src/static'), @@ -55,15 +82,32 @@ let webpackConfig = merge(baseWebpackConfig, { ]) ] }); -// const hotMiddlewareScript = 'webpack-hot-middleware/client?reload=true'; -const hotMiddlewareScript = 'webpack-dev-server/client?http://localhost:' + sysConfig.dev.serverPort; -for (const key of Object.keys(webpackConfig.entry)) { - webpackConfig.entry[key].unshift("babel-polyfill", hotMiddlewareScript, 'webpack/hot/dev-server'); + +// for (const key of Object.keys(webpackConfig.entry)) { +// webpackConfig.entry[key].unshift("babel-polyfill"); +// } +if (sysConfig.dev.screw_ie8) { + webpackConfig.plugins.push( + new webpack.HotModuleReplacementPlugin(), + new webpack.NoEmitOnErrorsPlugin() + ); } -webpackConfig.plugins.push( - new webpack.HotModuleReplacementPlugin(), - new webpack.NoEmitOnErrorsPlugin() -); + +// if (sysConfig.dev.screw_ie8) { +// // const hotMiddlewareScript = 'webpack-hot-middleware/client?reload=true'; +// const hotMiddlewareScript = 'webpack-dev-server/client?http://localhost:' + sysConfig.dev.serverPort; +// for (const key of Object.keys(webpackConfig.entry)) { +// webpackConfig.entry[key].unshift("babel-polyfill", hotMiddlewareScript, 'webpack/hot/dev-server'); +// } +// webpackConfig.plugins.push( +// new webpack.HotModuleReplacementPlugin(), +// new webpack.NoEmitOnErrorsPlugin() +// ); +// } else { +// for (const key of Object.keys(webpackConfig.entry)) { +// webpackConfig.entry[key].unshift("babel-polyfill"); +// } +// } const pages = Object.keys(utils.getEntry('src/views/**/*.html', 'src/views/')); pages.forEach(function (pathname) { diff --git a/build/webpack.prod.conf.js b/build/webpack.prod.conf.js index ad94407..5388f36 100644 --- a/build/webpack.prod.conf.js +++ b/build/webpack.prod.conf.js @@ -32,37 +32,43 @@ let webpackConfig = merge(baseWebpackConfig, { module: { rules: [ // js babel编译,团购项目需要支持ie8,所以暂时不用Babel编译 - { - test: /\.js$/, - use: [ - { - loader: 'babel-loader' - }, - { - loader: 'es3ify-loader' - } - ], - //resolve('node_modules/djcpsweb') - // include: [ - // resolve('src'), - // resolve('node_modules/webpack-build-server/client') - // ] - } + // { + // test: /\.js$/, + // exclude: /node_modules/, + // use: [ + // { + // loader: 'babel-loader' + // }, + // { + // loader: 'es3ify-loader' + // } + // ] + // //resolve('node_modules/djcpsweb') + // // include: [ + // // resolve('src'), + // // resolve('node_modules/webpack-build-server/client') + // // ] + // } ] }, plugins: [ - new UglifyJsPlugin({ // 压缩代码 - output: { - beautify: true,//有正常的空格和断句,注释也会保留, - comments: true, - keep_quoted_props: true - }, - screw_ie8: false, - compress: { - warnings: false, properties: false - }, - except: ['$super', '$', 'exports', 'require'] // 排除关键字 - }), + // new UglifyJsPlugin({ // 压缩代码 + // output: { + // screw_ie8: false, + // beautify: true, //有正常的空格和断句,注释也会保留, + // comments: true, + // keep_quoted_props: true + // }, + // screw_ie8: false, + // compress: { + // warnings: false, properties: false,screw_ie8: false + // }, + // mangle: { + // screw_ie8: false, + // except: ['$'] + // }, + // except: ['$super', '$', 'exports', 'require'] // 排除关键字 + // }), // new UglifyJSPlugin({ // compress: {screw_ie8: false}, // output: {screw_ie8: false}, @@ -72,6 +78,26 @@ let webpackConfig = merge(baseWebpackConfig, { // }, // support_ie8: true // }) + new webpack.optimize.UglifyJsPlugin({ // 压缩代码 + output: { + // screw_ie8: false, + beautify: true, //有正常的空格和断句,注释也会保留, + comments: true, + quote_keys: true, //SCRIPT1048: 缺少标识符 + keep_quoted_props: true // 是否保留对象字面量中的引号。 + }, + // screw_ie8: false, + compress: { + // screw_ie8: false, + warnings: false, + properties: false + }, + mangle: { + eval: true, + screw_ie8: false, //是否把支持IE8的代码clear掉 + except: ['$super', '$', 'exports', 'require'] // 排除关键字 + } + }), new CopyWebpackPlugin([ { from: path.resolve(__dirname, '../src/static'), diff --git a/build/webpack.temp.config.js b/build/webpack.temp.config.js deleted file mode 100644 index 5732848..0000000 --- a/build/webpack.temp.config.js +++ /dev/null @@ -1,196 +0,0 @@ -// 移除node开发环境,webpack警告 -process.noDeprecation = true; - -const path = require('path'); -const glob = require('glob'); -const webpack = require('webpack'); -const sysConfig = require('../sysConfig/index'); -const utils = require('./utils'); -const CopyWebpackPlugin = require('copy-webpack-plugin'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const CleanWebpackPlugin = require('clean-webpack-plugin'); -const CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin; -const UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; -const ExtractTextPlugin = require('extract-text-webpack-plugin'); - -const debug = process.env.NODE_ENV !== 'production'; -const relatePath = ''; -const entries = getEntry(relatePath + 'src/scripts/page/**/*.js', relatePath + 'src/scripts/page/'); -const chunks = Object.keys(entries); -const publicPath = debug ? `http://192.168.2.167:${sysConfig.dev.port}/` : `.${sysConfig.dev.publicPath}/`; -let webpackConfig = { - entry: entries, - output: { - // path: sysConfig.dev.outPutPath, - path: '/', - publicPath: publicPath, - filename: 'scripts/[name].js', - chunkFilename: 'scripts/[id].chunk.js?[chunkhash]' - }, - devtool: 'eval-source-map', - module: { - rules: [ - // ...utils.styleLoaders({sourceMap: sysConfig.dev.cssSourceMap, usePostCSS: true}), - { - test: /\.css$/, - use: ['style-loader', 'css-loader'] - }, - { - test: /\.less$/, - use: [{ - loader: "style-loader" - }, { - loader: "css-loader", options: { - sourceMap: true - } - }, { - loader: "less-loader", options: { - sourceMap: true - } - }] - }, - { - test: /\.html$/, - use: [{ - loader: 'html-loader', - options: { - minimize: false - } - }] - }, - // js babel编译,团购项目需要支持ie8,所以暂时不用Babel编译 - { - test: /\.js$/, - loader: 'babel-loader', - include: [ - resolve('src'), - // resolve('test'), - resolve('node_modules/webpack-hot-middleware'), - resolve('node_modules/webpack-dev-server/client') - ], - query: { - //处理IE8中Object.defineProperty报错的问题 - presets: ['es2015-loose'] - } - }, - { - test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, - loader: 'url-loader', - options: { - limit: 10000, - name: utils.assetsPath('img/[name].[hash:7].[ext]') - // publicPath: '../' - } - }, - { - test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, - loader: 'url-loader', - options: { - limit: 10000, - name: utils.assetsPath('media/[name].[hash:7].[ext]') - } - }, - { - test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, - loader: 'url-loader', - options: { - limit: 10000, - name: utils.assetsPath('fonts/[name].[hash:7].[ext]') - } - } - // , - // { - // test: /.art$/, - // use: ['art-template-loader'] - // } - ] - }, - plugins: [ - new CleanWebpackPlugin(['dist']), - new webpack.ProvidePlugin({ // 加载jq - $: 'jquery' - }), - new CommonsChunkPlugin({ - name: 'vendors', // 将公共模块提取,生成名为`vendors`的chunk - chunks: chunks, - minChunks: chunks.length // 提取所有entry共同依赖的模块 - }), - new ExtractTextPlugin('styles/[name].css'), // 单独使用link标签加载css并设置路径,相对于output配置中的publickPath - debug ? function () { - } : new UglifyJsPlugin({ // 压缩代码 - compress: { - warnings: false - }, - except: ['$super', '$', 'exports', 'require'] // 排除关键字 - }), - // todo 映射static - new CopyWebpackPlugin([ - { - from: path.resolve(__dirname, '../src/static'), - to: sysConfig.dev.assetsSubDirectory, - ignore: ['.*'] - } - ]) - ] -}; -const hotMiddlewareScript = 'webpack-hot-middleware/client?reload=true'; - -for (const key of Object.keys(webpackConfig.entry)) { - webpackConfig.entry[key].unshift("babel-polyfill", hotMiddlewareScript); -} -webpackConfig.plugins.push( - new webpack.HotModuleReplacementPlugin(), - new webpack.NoEmitOnErrorsPlugin() -); - -const pages = Object.keys(getEntry(relatePath + 'src/views/**/*.html', relatePath + 'src/views/')); -pages.forEach(function (pathname) { - const conf = { - filename: '../' + sysConfig.dev.tplPath + '/' + pathname + '.html', // 生成的html存放路径,相对于path - template: 'src/views/' + pathname + '.html', // html模板路径 - inject: false // js插入的位置,true/'head'/'body'/false - /* - * 压缩这块,调用了html-minify,会导致压缩时候的很多html语法检查问题, - * 如在html标签属性上使用{{...}}表达式,很多情况下并不需要在此配置压缩项, - * 另外,UglifyJsPlugin会在压缩代码的时候连同html一起压缩。 - * 为避免压缩html,需要在html-loader上配置'html?-minimize',见loaders中html-loader的配置。 - */ - // minify: { //压缩HTML文件 - // removeComments: true, //移除HTML中的注释 - // collapseWhitespace: false //删除空白符与换行符 - // } - }; - if (pathname in webpackConfig.entry) { - conf.favicon = path.resolve(__dirname, '../favicon.ico'); - conf.inject = 'body'; - conf.chunks = ['vendors', pathname]; - conf.hash = true; - } - webpackConfig.plugins.push(new HtmlWebpackPlugin(conf)); -}); - -module.exports = webpackConfig; - -function getEntry(globPath, pathDir) { - const files = glob.sync(globPath); - const entries = {}; - let {entry, dirname, basename, pathname, extname} = {}; - - for (let i = 0; i < files.length; i++) { - entry = files[i]; - dirname = path.dirname(entry); - extname = path.extname(entry); - basename = path.basename(entry, extname); - pathname = path.normalize(path.join(dirname, basename)); - pathDir = path.normalize(pathDir); - if (pathname.startsWith(pathDir)) { - pathname = pathname.substring(pathDir.length); - } - entries[pathname] = ['./' + entry]; - } - return entries; -} - -function resolve(dir) { - return path.join(__dirname, '..', dir); -} diff --git a/package.json b/package.json index 61ce7a1..f91107f 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "http-errors": "~1.6.2", "http-proxy": "^1.17.0", "morgan": "^1.9.1", - "webpack-hot-middleware-ie8": "^2.13.5" + "requirejs": "^2.3.6" }, "devDependencies": { "autoprefixer": "^7.1.2", @@ -40,6 +40,7 @@ "cross-env": "^3.0.0", "css-loader": "^0.23.1", "es3ify-loader": "^0.2.0", + "es3ify-webpack-plugin": "^0.1.0", "eslint": "^4.2.0", "eslint-config-djcps": "^0.0.4", "eslint-loader": "^1.9.0", @@ -54,7 +55,7 @@ "html-loader": "^0.4.3", "html-webpack-plugin": "^2.29.0", "http-proxy-middleware": "^0.19.1", - "jquery": "^3.3.1", + "jquery": "^1.12.4", "less": "^3.9.0", "less-loader": "^4.1.0", "resolve-url-loader": "^3.0.1", diff --git a/src/scripts/page/index.js b/src/scripts/page/index.js index 8fb68ea..ea7a19e 100644 --- a/src/scripts/page/index.js +++ b/src/scripts/page/index.js @@ -4,7 +4,7 @@ require('../../styles/common/global.css'); require('../../styles/common/grid.css'); require('../../styles/common/common.less'); require('../../styles/page/index.less'); - +// require('requirejs/require.js'); var oP = document.createElement('p'); oP.className = 'text'; oP.innerHTML = '这是由js生成的一句话。'; @@ -12,16 +12,22 @@ document.querySelector('.g-bd').appendChild(oP); /* eslint-disable no-undef */ // 增加事件 + $('#dialog').click(function () { require(['../components/dialog/index.js'], function (dialog) { dialog(); }); }); +// require.ensure([], function (require) { +// var dialog = require('../components/dialog/index.js'); +// dialog(); +// }); $('#http').click(function () { getTest(); // ajax('https://api.douban.com/v2/music/search?q=周杰伦'); }); getTest(); + function getTest() { $.ajax({ type: "post", diff --git a/src/styles/page/index.less b/src/styles/page/index.less index 5e71b32..440080e 100644 --- a/src/styles/page/index.less +++ b/src/styles/page/index.less @@ -8,7 +8,7 @@ height: 36px; line-height: 36px; background-color: #677D7C; - color: #0066ff; + color: #0093ff; text-align: center; border:none; box-shadow: 0 1px 3px #999; diff --git a/src/views/common/meta.html b/src/views/common/meta.html index ecc29c6..c213be6 100644 --- a/src/views/common/meta.html +++ b/src/views/common/meta.html @@ -7,9 +7,14 @@ - + + + + diff --git a/sysConfig/index.js b/sysConfig/index.js index 3c8dc3d..d83712e 100644 --- a/sysConfig/index.js +++ b/sysConfig/index.js @@ -18,7 +18,8 @@ module.exports = { assetsPublicPath: '/', // 'https://cdn.xxxxx.com', // 添加路径前缀,后续cdn扩展 assetsSubDirectory: 'static', //静态资源指向目录 // publicPath: '/static', - tplPath: 'temp_views' + tplPath: 'temp_views', + screw_ie8: false // outPutPath: path.join(__dirname, '../dist/static') }, @@ -33,6 +34,7 @@ module.exports = { // publicPath: '/static', // outPutPath: path.join(__dirname, '../dist/static'), tplPath: 'temp_views', - productionSourceMap: false + productionSourceMap: false, + screw_ie8: false } }; diff --git a/webpackDevServer.js b/webpackDevServer.js index bd117e9..33d496f 100644 --- a/webpackDevServer.js +++ b/webpackDevServer.js @@ -11,8 +11,8 @@ let serverPort = sysConfig.dev.serverPort || 2082; const options = { publicPath: '/', - hot: false, - inline: false, + hot: sysConfig.dev.screw_ie8, + inline: sysConfig.dev.screw_ie8, port: serverPort, host: sysConfig.dev.host, proxy: { From f17c847146575ae3e499e2b7001d70a107c00c00 Mon Sep 17 00:00:00 2001 From: csl <453826887@qq.com> Date: Fri, 8 Mar 2019 18:50:00 +0800 Subject: [PATCH 07/12] =?UTF-8?q?3.9=20opt=EF=BC=9Aie8=20=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E7=8E=AF=E5=A2=83=E8=B0=83=E8=AF=95=E5=AE=8C=E6=AF=95?= =?UTF-8?q?=EF=BC=8C=E5=9C=A8xp=20ie8=E4=B8=AD=E8=BF=98=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20=20promise=E4=B8=BA=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .babelrc | 2 +- build/base/files.js | 37 +++++ build/common.js | 4 +- build/webpack.base.conf.js | 49 ++++--- build/webpack.dev.conf.js | 85 +++++++---- build/webpack.prod.conf.js | 82 +++++++---- build/webpack.temp.config.js | 196 ------------------------- package.json | 9 +- src/scripts/components/dialog/index.js | 37 +++-- src/scripts/page/index.js | 14 +- src/scripts/page/main.js | 6 + src/scripts/page/temp.js | 12 ++ src/static/js/es6-promise.auto.min.js | 1 + src/styles/page/index.less | 2 +- src/utils/index.js | 7 + src/views/common/meta.html | 10 +- sysConfig/index.js | 8 +- webpackDevServer.js | 4 +- 18 files changed, 267 insertions(+), 298 deletions(-) create mode 100644 build/base/files.js delete mode 100644 build/webpack.temp.config.js create mode 100644 src/scripts/page/main.js create mode 100644 src/scripts/page/temp.js create mode 100644 src/static/js/es6-promise.auto.min.js create mode 100644 src/utils/index.js diff --git a/.babelrc b/.babelrc index 632e90e..34ddd78 100644 --- a/.babelrc +++ b/.babelrc @@ -27,7 +27,7 @@ "helpers": false, "polyfill": false, "regenerator": true -// "moduleName": "babel-runtime" + // "moduleName": "babel-runtime" } ] ] diff --git a/build/base/files.js b/build/base/files.js new file mode 100644 index 0000000..0698121 --- /dev/null +++ b/build/base/files.js @@ -0,0 +1,37 @@ +const path = require('path'); +/** + * 各类资源的路径,加入绝对路径保证各个层级的文件引用正常 + */ +module.exports = ((filesName) => { + const files = filesName; + // const appPath = path.resolve(files.root, files.appName); + // files.appPath = appPath; + // files.buildPath = path.resolve(files.root, files.buildName); + // files.dllPath = path.resolve(files.root, `${files.buildName}/dll`); + // files.jsPath = path.resolve(appPath, files.jsName); + // files.cssPath = path.resolve(appPath, files.cssName); + // files.imgPath = path.resolve(appPath, files.imgName); + // files.fontPath = path.resolve(appPath, files.fontName); + // files.viewPath = path.resolve(appPath, files.viewName); + // files.testPath = path.resolve(appPath, files.testName); + // files.htmlPath = path.resolve(appPath, files.htmlName);\ + // files.componentPath = path.resolve(appPath, files.componentName); + const rootPath = path.join(__dirname, '../../'); + files.appPath = path.resolve(rootPath, `src`); + files.rootPath = rootPath; + files.staticPath = path.resolve(rootPath, `src/${files.staticName}`); + return files; +})({ + // root: process.cwd(), // 根目录 + // appName: 'app', + // buildName: 'build', // 打包文件 + // componentName: 'component', // 公共组件文件 + // htmlName: 'html', // 视图文件 + // cssName: 'source/css', // 公共样式文件 + // fontName: 'source/font', // 公共字体文件 + // imgName: 'source/img', // 公共图片文件 + // jsName: 'source/js', // 公共脚本文件 + staticName: 'static' // 静态资源包文件 + // viewName: 'view', // 视图模板文件 + // testName: 'tests', // 测试文件 +}); diff --git a/build/common.js b/build/common.js index 4a5c473..2534c4c 100644 --- a/build/common.js +++ b/build/common.js @@ -2,8 +2,8 @@ var path = require('path'); var glob = require('glob'); -exports.getEntry = function (globPath, pathDir,options) { - var files = glob.sync(globPath,options); +exports.getEntry = function (globPath, pathDir, options) { + var files = glob.sync(globPath, options); var entries = {}, entry, dirname, basename, pathname, extname; for (var i = 0; i < files.length; i++) { diff --git a/build/webpack.base.conf.js b/build/webpack.base.conf.js index 84ae846..b6e4700 100644 --- a/build/webpack.base.conf.js +++ b/build/webpack.base.conf.js @@ -1,19 +1,28 @@ // 移除node开发环境,webpack警告 process.noDeprecation = true; -// const path = require('path'); +const path = require('path'); const webpack = require('webpack'); const sysConfig = require('../sysConfig'); const utils = require('./utils'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin; -// const UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; +const merge = require('webpack-merge'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); +const Es3ifyPlugin = require('es3ify-webpack-plugin'); +const files = require('./base/files'); +// const entries = merge(utils.getEntry('src/scripts/page/**/*.js', 'src/scripts/page/'), { +// 'Main': [path.resolve(files.appPath, 'utils/index.js')] +// }); const entries = utils.getEntry('src/scripts/page/**/*.js', 'src/scripts/page/'); const chunks = Object.keys(entries); +// function resolve(dir) { +// return path.join(__dirname, '..', dir); +// } + let webpackConfig = { entry: entries, output: { @@ -25,7 +34,23 @@ let webpackConfig = { }, module: { rules: [ - // ...utils.styleLoaders({sourceMap: sysConfig.dev.cssSourceMap, usePostCSS: true}), + { + test: /\.js$/, + exclude: /(node_modules)/, + //include: path.join(projectDirname, 'src'), + include: [files.staticPath], + use: { + loader: 'babel-loader', + /*options: { + presets: ['env'] + }*/ + options: { + presets: ['env', 'es2015-loose'] + //presets: ['env'], + //plugins: ['transform-runtime', 'proxy'] + } + } + }, { test: /\.css$/, use: ['style-loader', 'css-loader'] @@ -36,6 +61,7 @@ let webpackConfig = { loader: "style-loader" }, { loader: "css-loader", options: { + // todo dev true ?pro false? sourceMap: true } }, { @@ -53,17 +79,6 @@ let webpackConfig = { } }] }, - // js babel编译,团购项目需要支持ie8,所以暂时不用Babel编译 - // { - // test: /\.js$/, - // loader: 'babel-loader', - // //resolve('node_modules/djcpsweb') - // include: [ - // resolve('src'), - // resolve('test'), - // resolve('node_modules/webpack-dev-server/client') - // ] - // }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader', @@ -88,14 +103,10 @@ let webpackConfig = { name: utils.assetsPath('fonts/[name].[hash:7].[ext]') } } - // , - // { - // test: /.art$/, - // use: ['art-template-loader'] - // } ] }, plugins: [ + new Es3ifyPlugin(), new CleanWebpackPlugin(['dist']), new webpack.ProvidePlugin({ // 加载jq $: 'jquery' diff --git a/build/webpack.dev.conf.js b/build/webpack.dev.conf.js index 9c5e483..d2a8259 100644 --- a/build/webpack.dev.conf.js +++ b/build/webpack.dev.conf.js @@ -9,14 +9,12 @@ const merge = require('webpack-merge'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const baseWebpackConfig = require('./webpack.base.conf'); - -const publicPath = `http://192.168.2.167:${sysConfig.dev.port}/`; +const files = require('./base/files'); function resolve(dir) { return path.join(__dirname, '..', dir); } -// todo let webpackConfig = merge(baseWebpackConfig, { // output: { // // path: sysConfig.dev.outPutPath, @@ -25,27 +23,43 @@ let webpackConfig = merge(baseWebpackConfig, { // filename: 'scripts/[name].js', // chunkFilename: 'scripts/[id].chunk.js?[chunkhash]' // }, - devtool: 'cheap-module-eval-source-map', + //ps:inline-source-map 其他方式(cheap-module-eval-source-map)压缩会导致开发环境在ie8下,编译异常 取消标识符,坑爹:注意webpack重启才能检验,忘记已经去除ie8 热更新了 + devtool: 'inline-source-map', module: { rules: [ - // js babel编译,团购项目需要支持ie8,所以暂时不用Babel编译 - { - test: /\.js$/, - loader: 'babel-loader', - include: [ - resolve('src'), - // resolve('test'), - resolve('node_modules/webpack-hot-middleware'), - resolve('node_modules/webpack-dev-server/client') - ], - query: { - //处理IE8中Object.defineProperty报错的问题 - presets: ['es2015-loose'] - } - } + // { + // test: /\.js$/, + // enforce: "post", + // // enforce: "pre", + // include: [ + // resolve('src'), + // // resolve('test'), + // // resolve('node_modules/webpack-hot-middleware'), + // resolve('node_modules/webpack-dev-server/client') + // ], + // loader: "es3ify-loader" + // } ] }, plugins: [ + // new webpack.optimize.UglifyJsPlugin({ // 压缩代码 + // output: { + // screw_ie8: false, + // beautify: true, //有正常的空格和断句,注释也会保留, + // comments: true, + // quote_keys: true, + // keep_quoted_props: true + // }, + // screw_ie8: false, + // compress: { + // warnings: false, properties: false, screw_ie8: false + // }, + // mangle: { + // eval: true, + // screw_ie8: false, + // except: ['$super', '$', 'exports', 'require'] // 排除关键字 + // } + // }), new CopyWebpackPlugin([ { from: path.resolve(__dirname, '../src/static'), @@ -55,15 +69,32 @@ let webpackConfig = merge(baseWebpackConfig, { ]) ] }); -// const hotMiddlewareScript = 'webpack-hot-middleware/client?reload=true'; -const hotMiddlewareScript = 'webpack-dev-server/client?http://localhost:' + sysConfig.dev.serverPort; -for (const key of Object.keys(webpackConfig.entry)) { - webpackConfig.entry[key].unshift("babel-polyfill", hotMiddlewareScript, 'webpack/hot/dev-server'); + +// for (const key of Object.keys(webpackConfig.entry)) { +// webpackConfig.entry[key].unshift("babel-polyfill"); +// } +if (sysConfig.dev.screw_ie8) { + webpackConfig.plugins.push( + new webpack.HotModuleReplacementPlugin(), + new webpack.NoEmitOnErrorsPlugin() + ); } -webpackConfig.plugins.push( - new webpack.HotModuleReplacementPlugin(), - new webpack.NoEmitOnErrorsPlugin() -); + +// if (sysConfig.dev.screw_ie8) { +// // const hotMiddlewareScript = 'webpack-hot-middleware/client?reload=true'; +// const hotMiddlewareScript = 'webpack-dev-server/client?http://localhost:' + sysConfig.dev.serverPort; +// for (const key of Object.keys(webpackConfig.entry)) { +// webpackConfig.entry[key].unshift("babel-polyfill", hotMiddlewareScript, 'webpack/hot/dev-server'); +// } +// webpackConfig.plugins.push( +// new webpack.HotModuleReplacementPlugin(), +// new webpack.NoEmitOnErrorsPlugin() +// ); +// } else { +// for (const key of Object.keys(webpackConfig.entry)) { +// webpackConfig.entry[key].unshift("babel-polyfill"); +// } +// } const pages = Object.keys(utils.getEntry('src/views/**/*.html', 'src/views/')); pages.forEach(function (pathname) { diff --git a/build/webpack.prod.conf.js b/build/webpack.prod.conf.js index ad94407..5388f36 100644 --- a/build/webpack.prod.conf.js +++ b/build/webpack.prod.conf.js @@ -32,37 +32,43 @@ let webpackConfig = merge(baseWebpackConfig, { module: { rules: [ // js babel编译,团购项目需要支持ie8,所以暂时不用Babel编译 - { - test: /\.js$/, - use: [ - { - loader: 'babel-loader' - }, - { - loader: 'es3ify-loader' - } - ], - //resolve('node_modules/djcpsweb') - // include: [ - // resolve('src'), - // resolve('node_modules/webpack-build-server/client') - // ] - } + // { + // test: /\.js$/, + // exclude: /node_modules/, + // use: [ + // { + // loader: 'babel-loader' + // }, + // { + // loader: 'es3ify-loader' + // } + // ] + // //resolve('node_modules/djcpsweb') + // // include: [ + // // resolve('src'), + // // resolve('node_modules/webpack-build-server/client') + // // ] + // } ] }, plugins: [ - new UglifyJsPlugin({ // 压缩代码 - output: { - beautify: true,//有正常的空格和断句,注释也会保留, - comments: true, - keep_quoted_props: true - }, - screw_ie8: false, - compress: { - warnings: false, properties: false - }, - except: ['$super', '$', 'exports', 'require'] // 排除关键字 - }), + // new UglifyJsPlugin({ // 压缩代码 + // output: { + // screw_ie8: false, + // beautify: true, //有正常的空格和断句,注释也会保留, + // comments: true, + // keep_quoted_props: true + // }, + // screw_ie8: false, + // compress: { + // warnings: false, properties: false,screw_ie8: false + // }, + // mangle: { + // screw_ie8: false, + // except: ['$'] + // }, + // except: ['$super', '$', 'exports', 'require'] // 排除关键字 + // }), // new UglifyJSPlugin({ // compress: {screw_ie8: false}, // output: {screw_ie8: false}, @@ -72,6 +78,26 @@ let webpackConfig = merge(baseWebpackConfig, { // }, // support_ie8: true // }) + new webpack.optimize.UglifyJsPlugin({ // 压缩代码 + output: { + // screw_ie8: false, + beautify: true, //有正常的空格和断句,注释也会保留, + comments: true, + quote_keys: true, //SCRIPT1048: 缺少标识符 + keep_quoted_props: true // 是否保留对象字面量中的引号。 + }, + // screw_ie8: false, + compress: { + // screw_ie8: false, + warnings: false, + properties: false + }, + mangle: { + eval: true, + screw_ie8: false, //是否把支持IE8的代码clear掉 + except: ['$super', '$', 'exports', 'require'] // 排除关键字 + } + }), new CopyWebpackPlugin([ { from: path.resolve(__dirname, '../src/static'), diff --git a/build/webpack.temp.config.js b/build/webpack.temp.config.js deleted file mode 100644 index 5732848..0000000 --- a/build/webpack.temp.config.js +++ /dev/null @@ -1,196 +0,0 @@ -// 移除node开发环境,webpack警告 -process.noDeprecation = true; - -const path = require('path'); -const glob = require('glob'); -const webpack = require('webpack'); -const sysConfig = require('../sysConfig/index'); -const utils = require('./utils'); -const CopyWebpackPlugin = require('copy-webpack-plugin'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const CleanWebpackPlugin = require('clean-webpack-plugin'); -const CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin; -const UglifyJsPlugin = webpack.optimize.UglifyJsPlugin; -const ExtractTextPlugin = require('extract-text-webpack-plugin'); - -const debug = process.env.NODE_ENV !== 'production'; -const relatePath = ''; -const entries = getEntry(relatePath + 'src/scripts/page/**/*.js', relatePath + 'src/scripts/page/'); -const chunks = Object.keys(entries); -const publicPath = debug ? `http://192.168.2.167:${sysConfig.dev.port}/` : `.${sysConfig.dev.publicPath}/`; -let webpackConfig = { - entry: entries, - output: { - // path: sysConfig.dev.outPutPath, - path: '/', - publicPath: publicPath, - filename: 'scripts/[name].js', - chunkFilename: 'scripts/[id].chunk.js?[chunkhash]' - }, - devtool: 'eval-source-map', - module: { - rules: [ - // ...utils.styleLoaders({sourceMap: sysConfig.dev.cssSourceMap, usePostCSS: true}), - { - test: /\.css$/, - use: ['style-loader', 'css-loader'] - }, - { - test: /\.less$/, - use: [{ - loader: "style-loader" - }, { - loader: "css-loader", options: { - sourceMap: true - } - }, { - loader: "less-loader", options: { - sourceMap: true - } - }] - }, - { - test: /\.html$/, - use: [{ - loader: 'html-loader', - options: { - minimize: false - } - }] - }, - // js babel编译,团购项目需要支持ie8,所以暂时不用Babel编译 - { - test: /\.js$/, - loader: 'babel-loader', - include: [ - resolve('src'), - // resolve('test'), - resolve('node_modules/webpack-hot-middleware'), - resolve('node_modules/webpack-dev-server/client') - ], - query: { - //处理IE8中Object.defineProperty报错的问题 - presets: ['es2015-loose'] - } - }, - { - test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, - loader: 'url-loader', - options: { - limit: 10000, - name: utils.assetsPath('img/[name].[hash:7].[ext]') - // publicPath: '../' - } - }, - { - test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, - loader: 'url-loader', - options: { - limit: 10000, - name: utils.assetsPath('media/[name].[hash:7].[ext]') - } - }, - { - test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, - loader: 'url-loader', - options: { - limit: 10000, - name: utils.assetsPath('fonts/[name].[hash:7].[ext]') - } - } - // , - // { - // test: /.art$/, - // use: ['art-template-loader'] - // } - ] - }, - plugins: [ - new CleanWebpackPlugin(['dist']), - new webpack.ProvidePlugin({ // 加载jq - $: 'jquery' - }), - new CommonsChunkPlugin({ - name: 'vendors', // 将公共模块提取,生成名为`vendors`的chunk - chunks: chunks, - minChunks: chunks.length // 提取所有entry共同依赖的模块 - }), - new ExtractTextPlugin('styles/[name].css'), // 单独使用link标签加载css并设置路径,相对于output配置中的publickPath - debug ? function () { - } : new UglifyJsPlugin({ // 压缩代码 - compress: { - warnings: false - }, - except: ['$super', '$', 'exports', 'require'] // 排除关键字 - }), - // todo 映射static - new CopyWebpackPlugin([ - { - from: path.resolve(__dirname, '../src/static'), - to: sysConfig.dev.assetsSubDirectory, - ignore: ['.*'] - } - ]) - ] -}; -const hotMiddlewareScript = 'webpack-hot-middleware/client?reload=true'; - -for (const key of Object.keys(webpackConfig.entry)) { - webpackConfig.entry[key].unshift("babel-polyfill", hotMiddlewareScript); -} -webpackConfig.plugins.push( - new webpack.HotModuleReplacementPlugin(), - new webpack.NoEmitOnErrorsPlugin() -); - -const pages = Object.keys(getEntry(relatePath + 'src/views/**/*.html', relatePath + 'src/views/')); -pages.forEach(function (pathname) { - const conf = { - filename: '../' + sysConfig.dev.tplPath + '/' + pathname + '.html', // 生成的html存放路径,相对于path - template: 'src/views/' + pathname + '.html', // html模板路径 - inject: false // js插入的位置,true/'head'/'body'/false - /* - * 压缩这块,调用了html-minify,会导致压缩时候的很多html语法检查问题, - * 如在html标签属性上使用{{...}}表达式,很多情况下并不需要在此配置压缩项, - * 另外,UglifyJsPlugin会在压缩代码的时候连同html一起压缩。 - * 为避免压缩html,需要在html-loader上配置'html?-minimize',见loaders中html-loader的配置。 - */ - // minify: { //压缩HTML文件 - // removeComments: true, //移除HTML中的注释 - // collapseWhitespace: false //删除空白符与换行符 - // } - }; - if (pathname in webpackConfig.entry) { - conf.favicon = path.resolve(__dirname, '../favicon.ico'); - conf.inject = 'body'; - conf.chunks = ['vendors', pathname]; - conf.hash = true; - } - webpackConfig.plugins.push(new HtmlWebpackPlugin(conf)); -}); - -module.exports = webpackConfig; - -function getEntry(globPath, pathDir) { - const files = glob.sync(globPath); - const entries = {}; - let {entry, dirname, basename, pathname, extname} = {}; - - for (let i = 0; i < files.length; i++) { - entry = files[i]; - dirname = path.dirname(entry); - extname = path.extname(entry); - basename = path.basename(entry, extname); - pathname = path.normalize(path.join(dirname, basename)); - pathDir = path.normalize(pathDir); - if (pathname.startsWith(pathDir)) { - pathname = pathname.substring(pathDir.length); - } - entries[pathname] = ['./' + entry]; - } - return entries; -} - -function resolve(dir) { - return path.join(__dirname, '..', dir); -} diff --git a/package.json b/package.json index 61ce7a1..9453cb5 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "main": "index.js", "scripts": { - "start": "concurrently \"node expressServer\" \"node webpackDevServer\"", + "start": "concurrently \"node webpackDevServer\" \"node expressServer\"", "build:webpack": "cross-env NODE_ENV=production webpack --config build/webpack.prod.conf.js --progress --hide-modules", "node:compile": "node build/compile_node/compile-art-template", "build": "npm run build:webpack&&npm run node:compile" @@ -17,11 +17,13 @@ "cookie-parser": "^1.4.3", "cors": "^2.8.5", "debug": "~2.6.9", + "es5-shim": "^4.5.9", + "es6-promise": "^4.1.0", "express": "^4.16.4", "http-errors": "~1.6.2", "http-proxy": "^1.17.0", "morgan": "^1.9.1", - "webpack-hot-middleware-ie8": "^2.13.5" + "requirejs": "^2.3.6" }, "devDependencies": { "autoprefixer": "^7.1.2", @@ -40,6 +42,7 @@ "cross-env": "^3.0.0", "css-loader": "^0.23.1", "es3ify-loader": "^0.2.0", + "es3ify-webpack-plugin": "^0.1.0", "eslint": "^4.2.0", "eslint-config-djcps": "^0.0.4", "eslint-loader": "^1.9.0", @@ -54,7 +57,7 @@ "html-loader": "^0.4.3", "html-webpack-plugin": "^2.29.0", "http-proxy-middleware": "^0.19.1", - "jquery": "^3.3.1", + "jquery": "^1.12.4", "less": "^3.9.0", "less-loader": "^4.1.0", "resolve-url-loader": "^3.0.1", diff --git a/src/scripts/components/dialog/index.js b/src/scripts/components/dialog/index.js index 199f541..04bb7ca 100644 --- a/src/scripts/components/dialog/index.js +++ b/src/scripts/components/dialog/index.js @@ -1,16 +1,27 @@ // 加载模块css -require('./css/dialog.css'); -// 加载模板 -var html = require('./tmpl/dialog.html'); - -/* eslint-disable no-undef */ +// require('./css/dialog.css'); +// // 加载模板 +// var html = require('./tmpl/dialog.html'); +// +// /* eslint-disable no-undef */ +// module.exports = function () { +// var $dialog = $(html).clone(); +// $dialog.find('.close').on('click', function () { +// $dialog.fadeOut(function () { +// $(this).remove(); +// }); +// }); +// $('body').append($dialog); +// $dialog.fadeIn(); +// }; module.exports = function () { - var $dialog = $(html).clone(); - $dialog.find('.close').on('click', function () { - $dialog.fadeOut(function () { - $(this).remove(); - }); - }); - $('body').append($dialog); - $dialog.fadeIn(); + // var $dialog = $(html).clone(); + // $dialog.find('.close').on('click', function () { + // $dialog.fadeOut(function () { + // $(this).remove(); + // }); + // }); + // $('body').append($dialog); + // $dialog.fadeIn(); + console.log('123124'); }; diff --git a/src/scripts/page/index.js b/src/scripts/page/index.js index 8fb68ea..f264140 100644 --- a/src/scripts/page/index.js +++ b/src/scripts/page/index.js @@ -12,16 +12,28 @@ document.querySelector('.g-bd').appendChild(oP); /* eslint-disable no-undef */ // 增加事件 + $('#dialog').click(function () { - require(['../components/dialog/index.js'], function (dialog) { + // require(['../components/dialog/index.js'], function (dialog) { + // dialog(); + // }); + require(['./dialog/index.js'], function (dialog) { dialog(); }); + // require(['./temp.js'], function (dialog) { + // dialog(); + // }); }); +// require.ensure([], function (require) { +// var dialog = require('../components/dialog/index.js'); +// dialog(); +// }); $('#http').click(function () { getTest(); // ajax('https://api.douban.com/v2/music/search?q=周杰伦'); }); getTest(); + function getTest() { $.ajax({ type: "post", diff --git a/src/scripts/page/main.js b/src/scripts/page/main.js new file mode 100644 index 0000000..83c8613 --- /dev/null +++ b/src/scripts/page/main.js @@ -0,0 +1,6 @@ +/** + * 基础补丁包 + * */ +require('es5-shim'); +require('es5-shim/es5-sham'); +// require('es6-promise/auto');//文件头部定义 diff --git a/src/scripts/page/temp.js b/src/scripts/page/temp.js new file mode 100644 index 0000000..3f3da07 --- /dev/null +++ b/src/scripts/page/temp.js @@ -0,0 +1,12 @@ +/* eslint-disable no-undef */ +module.exports = function () { + // var $dialog = $(html).clone(); + // $dialog.find('.close').on('click', function () { + // $dialog.fadeOut(function () { + // $(this).remove(); + // }); + // }); + // $('body').append($dialog); + // $dialog.fadeIn(); + console.log('123124'); +}; diff --git a/src/static/js/es6-promise.auto.min.js b/src/static/js/es6-promise.auto.min.js new file mode 100644 index 0000000..77596bd --- /dev/null +++ b/src/static/js/es6-promise.auto.min.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.ES6Promise=e()}(this,function(){"use strict";function t(t){var e=typeof t;return null!==t&&("object"===e||"function"===e)}function e(t){return"function"==typeof t}function n(t){B=t}function r(t){G=t}function o(){return function(){return process.nextTick(a)}}function i(){return"undefined"!=typeof z?function(){z(a)}:c()}function s(){var t=0,e=new J(a),n=document.createTextNode("");return e.observe(n,{characterData:!0}),function(){n.data=t=++t%2}}function u(){var t=new MessageChannel;return t.port1.onmessage=a,function(){return t.port2.postMessage(0)}}function c(){var t=setTimeout;return function(){return t(a,1)}}function a(){for(var t=0;tvL+@)>=Shnyj#{G8Nd$ge&Wv3j_dI`; zcppc?KjPlDHjm4lkdax%d^Cp4mK|_pkz2l}^#L@g|skH~vu$SaBhbgQ@4Wu$~ 2 zzq{w>nXNTL_ttsmO2baK-7!Trqh=4+fPlGr!#B96CBTc?NAN{FzzeRIMsV%|e*2-j z>e&WLd~uhyyFM7?NuVux-|&k4nVkC048`Q_^0f&Ma(qv1e%l9ye%Z!Mz;)Ul>PE}I zX_PwsEF_#7%vZx}*FZA9IH{~mH={f13ZrhJJMWfd=ze9tvf`hNM;`QC4gG}9d}_$7 zwBwupGWcns!ZtlRYBP1EUO|5Wd-&H{f4giPsj%Dg!6x g{JWR%e=p#{4}EW41!)4e}-$X*VP#_b-5e|ATA-QFdsjGwI04t@O@37>oiO zdY^_uKSVkz#=O8WF6@t=WMSszv>+>Tnb5!cLTU&fu{G9Qt6gCv9Pk)c{son< z7L+FHzGq30KWD?mguCEDM~7s`n3j{CzQ*0^{PwyVFnZOxk50x12;I2-LOl8L?r^;R zrr2z{s=Rn<$y@mTJB?!VN7D*E+f I*Igp)slY?_Dy?AJHhx^RZNyPRCgb2JGbhGEe@;vV3q3(S!7C?)OEjq=Dc zcEqLy0&pcF0VjCT%->xO;LiI@!SK#!kguE697XUPjw`R(e_vlLp5}ApHmq***-k)< zt?=u8{#n2lqRXjnwz019)8OpnN`v9Mls|KNlo`AHJQ)fX%um`u7b#!-lsB4*wJVn# zvpfj~`XkL9!+&t5rLY<37#hRhPEF}?&d!E;d~mkQXw=T}KlM#EwYIV@EG)uDKh_tj z66F=<=Im^(s4qz>83KJ~SPs{%TqsRSKiX1hO=PpUV`WuV!Xq9pTyye2<>P$BgxLaO z=Ad(a?Z$`M{_Z0d^mJe+@grl6JTdAQBHBc7RLEF`gIS;UyT^NNvTvW|y96VEYSQ0z zut5-0rG!=0>n6sa0z#VWHq4(6Q3F$6n@LfMj`6GUNszIt>R}CC@zB``pvJdpu?AgS z `xl=y=*Ct` (W2RJ=+jRCPE`;)>^KcEDV;%iA)H^ z6MBs8=uG^=FoSFhCsiZV6Y7sD1w|=t8Xi3G>xFY0==u9m!e-6{e&B&F@lz_yZ+#T8 zNJ24(>h@xqjU mRb=DrqAl9pGZfRy!m^n4PTBtCn7xd#vdwgu{D6iN1oHwB| zi4RuZxg32;M#jeGtz36e!j5&YJ9%H;gWPkyQ&%K%Oq7K-SuGn!o`TyvH*fN~*l%Oe z`~x40z~}A7q&2QEz&i*YKvo5B#b8b`M&_3;p=8k-+(p!9NOho(reTidq6z{ll_3)7 zJBWg?Tq5MUNE_pLMwim?j)mN4o@JdNs;$96Jf1 `Hib?(WDxD^}^Yim`N*6;H<`C^xu30jhs1e !P6Z%SlvOQ(8`J|;Fw)JnU|;0{^oIWTeirma8u>C zk+GwFxlDbB+cq57J%6>rRyp5Xheg>+ErRZDGtJPw@WiJ4gN!=w0uNESOwru$WdqTm zS0S&=eqsN8d_1R$^221xs}RPKSlb>H_ocEf E1~Q1N1B8w= zhnRUU)p&60@LH$8x8puRmkr=!#H?hwr@s=|4LC@Ptsxz5-q6^<(%aRrI;|(unt$Zz z@>pAZEk$R&l =6G$ z dSI^w2Ov u#&G03mdx~zP{LkVbFA}NOF)2fSL)w5OBe?+ zattO$>SDU+fz%V~RL2 8xfBB*X-Q*Ko|tWgh;O`?Hn%1~S=)sn~9 zE3|T3`hER7W_v?YHumFAKDoRFXY{Dzu)*#1QVRX&{=1-a=p~83);7cP4 (hVdwNj9-a?Utz>1ddp$0#U6sYKDyu;HCsW5D} z^ALdQjcjwdKA29Vk997q i)e_rZlQ95qJZYBpGSi?YxlqIf($E1@qY+Xa 37}CvbGtGZ|Fc-=N6>nT0sP_{p~{pN@>}d10liBmIwu655d=fu~cZp!?q5+)=aU z-YYgeaXFqibC;JuR&k?VBg7 tkB zU`&F#q!coUS2FZZK3r`Czu>cG4=!dIr$U?!TKO$4E!XNq#Uhf0P1rzlci|)@2S=wK zgx+@I4Mz^c^Sf8|YKwuO;Kv@scEgtvC_2EtQl;o7LRRD$y4dEuxRj^ZR1x}nAPy$3 zPr*%aWn)lKV|REe%=33MKnI=47~t>nkRdkpVqe87hzs6Z3&&7ye?}RErIbmE4vD}m zDl%9o{!p?~;Mn_%bT-)BH+v7h?j}}It$nWP>=i!fl0In0 A>rHeJrm1`dohapkyxEE)V+~nCZtvr?% z{Q#@0p+K-%^@sA{g={67D)XYmcgx8x{=^IjJ*Qs9b?t@?O7`yNy &^R zYi)XW8Wt9&ML}5nYwY^E{%GM({p01#d%BDyDa2S05_ofx8I@aSnV)o=9`115u$whw zn LEb7KuwtM zZCjU>zhIWyPMt_hPuT@y5sAU6DUR`uj^Ldz7mcBiktac}%?sGS8-fK`ae|fp2^#M( z5KT@w$S@AQ^GIPo`LFaYz2<62=t6C`Ai>%K4Mq0( 2PJ`KH~%E*F(p^KlX*xW z%xd+%_;6_{UpDSn`px*bqx5^gjc0FWI^$=kBF2)lLg 8zza}kgG2`4GgVlhtP zIPV $&MZ*aEozfPob$-<1JD3I0(gYoB32 z!mAIh*Y{_1G#EZ@9D)MHf;Uvl!6AUy5c*uY#1I-U*I01^OrItj<#7)Kms##rK96&; z+9fjtm7@0<`uh8mS*^`(8qSU0;}c_(O1=GnPxeX6Gtb%MNh_*BPyUOZs#z_fiQ|j0 zhD_SX56?ZPzKaY|GdohbM&%=4QFPG8Br)!-YTwDbxw_S~)$ ?$e_#Y%c?e{UTWM=+ ?UO_XgiySkByDFp)CJ4fL~l&{9T9Y{xW}ZGJb-6w|W cc7v&>|Sm=7?tFNvxKd@+|WxJ2Nq9C^2 zY-WmclpK77fBmIOYymYK{)wv>BoSu^M76}syHJMB|ABaR0UxLYwMGLMpPLoDr3VSM zS{LL1oD-u#UxG@7`AnZpsa#2=%jse?Xi!M$BB^}VU@#p7xtJ-=t1|WrAeYA7`dJg& zcQz8CMo`X>Wh@E*^8s~S+f!~tEGhq21v(`|cujB8g*s(*419e2^}Ag33AWQ>ZsIlu zlBJ+{jJEdnEcgiF#17BxfpUGOBsX1k96t7pVI(646EinAGqVtZI}}Mawn#BFla#Wd zUAMITht<}7&s7%#{e*(|1tZmE3?s4IHOxizD CuYn9K(!-((n$6AvA4m!9Gpyvo{>-{Pvki@iO zI4a`4nd!JR3Xj&+w6D_ntAw|!)43s?v)|SPRcLf%ShICCTyg)3-(e-`eUu%%OWWEZ zC`w8scjvPCrXIC-Dkb?&b0kTj7_!tXvtzq8c2v_2^#HITej|5zdA-JAXjw^J>vwPZ z%ZD}^`HYN=> `+;C4N@P@nc#4!M2zUZAMvMRlxRBAr1LFmxwqBa*!&6b)gbBsy+MT2J zfPE-`QDy}HRSzf$vIjoTPB}hA+Tt6)AsS(j_Y!ZiX};c~zcx?@*C*SOZ_$j9&Vj3I zl}Bm8b99#lthLYp8;i+5&7~ZZCB*}+k7Z(KW=G_}nx*W$e4^Lv_HvmtGtfZAG&&l6 zm&JGCZTz%?Y&}Vb9i_Mc1|C2SIypFW@wqS>5PT B#*5BL=A8uXm{IJy}Q7k3#Xg6QN%YFDHPf%{a+PhtI zwj|4=E7n_%6Hweu0gb%6GwspSRjFh=U#u^MF;|1|Wf&S^D?m-0;g=4>`v{*wwjGtu zjt)FF__k>jiV9c=8ttD30}{lCi7f@hj46`y TWsBFDvf-|{9$bUwa8)d8QZavdnE9@ENW!E4_CCIcgs|Rc%fh0a{&{NgG!M7q z8TL#}GOdQ?^HxPgT^$eQ&g@(jCL%A_cglwFQl$%afCo(yP*NX^J4woM_?>&KWFk6X zqCOeU+rrK5>n;S;o1dw3Yo>_|p^=YOY=!G8a~%C<*_>aGJ-qY$EYu_mer#~;hOT{I zY*UUGkGoSoA1A`q3z!5QpPzjBoFDI0LB@F<96Nv#vR^%>0{R^^t<6B- nVdTI2V@_>ANuLUqK(b=`m>0SoM-TK#x+ z5K*oDydWve_Dkt8q$yAu9F3_4=<{>*Y^OF@$>k2MK_JO}&sbWVDe)~SNk4-)Bh3Tr z`QA&`xH+(wHWb@(1%&jFDQO^P*)>;dJxOpf(TuOU-r@`WeU)2TX=-Z=i|;O!HjhS^ zxwX39^I$_f)V1Egi+!HRbGF)2y6_R$kTr^rGRo@c>`r3%RSl 3a{7e zgSNIQH608>-^d8+u}SbY3zrF?8wCvD`WifmN)v%ne;K_83=rSgoSe3Q4}M}H{1hqY zw;vXPh=794h45rfVt_*-4!iZbB?01@BDeP0lZ@#9c` @O{@)7*S{XZ$vBdz)-~02P75_>gZ=yQEis zI!R&>bbc0WO_5ydI9tv9Ib JFFxQThYE72H63Z$YY zL^1p#5X*maNub4irKh*yB%614>~6e0N0tA~tdQ+e=~!AL#IHLgNW_9_Q||-8KzI`5 z2(naar$axXlZj{!?Q1)&etccBW;1L)PKO(-8M#^{}yFrPYD=rZDn2 zrVZmCu&oM?G^NF6x%7^wuu&Fst+1pmY^6pKWJ*vT62ialxJ*5dZ+`>Bmwkykuq4sF z$NxZL7LL$|PC^GUxdNPE2fB&B*{Vi7#3rWQjaJ~3nh*sCA_dI8WKZohJ`%db &g@|wQ&LG6a-_+imnkv|?PoLA6hW`&)I&b) zN&{Wv_LGp5$(rF%0a0FFK|$y$C%wX4;u}3&5YA6Ax bzdP9;{qc zVf|C}tY9<%ZU5mceg%^T+nfsZ<#%wmJy+(>b$S?w@75+Xw-JPZKc8{Gno3l0GIr)U z-`GE6LtOBqVSt%(7sTTzO#nwG2P8y_O!UY6fXW@=!b$o%FF5AxKA2Zsa_=CjUU4?< zb%A2OI2?_YxnY(L4^V0DoI(+^UrDCXkW2*}U=h_8Z39Hrun@mPg+2ETC(<{L6xc6R za@%uf?_zysJU5U?0t ByT^%oFsUn75eEQRq>j`8e1Ho}W#lHS5!|0X5A&No5%4gpUUbu&~_FJz? zg=yPFp}@ZTYaJTt5XH8o{k;$nW0Fx~%(2vyY%x0yBb}<>Q0QOGCDq`EN*PLMkW;79 z1s1# 1SsZcrN!k0Yp~JbZ9KO@U;2 zgr Hc6vOzFQCO=_v-&1nZ`fB&Y zQ3=72&tx%nEm+rUnMjN8y8wKdqaaxXrC^FvY`A{7*^~LNkSEQu9Mq{4PqS3aH^DAI zULy2B>09^*42ka;O41bO=@=bp2&9SNg$Y1S+P@O;H8fzn%(EsE@Bq_WoWOO$&rWyx zkm=(p^?ph#n!@8abR)s WGT@YEEwwR zpLi@Mn?gZjNCR16V@0-i@esZaRcCfxwTM##cs!?u`=o%8VXAxv3$DTv6vJPKqS0WT ze=uNy048uCpqBCv4cn$VOI>2(qh!m+uK*AepQ4u5EXPJ}@zDE>d9;}=F-E_GEizDT zZvC_Ck8 9dLCUaH+qMZDWD_y}*)NttOW71088^C3j-i$W2SZ|(BUEXBDIGm-HE9nZCXU}&{ z1=#DBc0Xile4S87fl$^zvwOm5)uuv5JA~oX9tmq+4hvok(tzv)1={yfTyWpR-w-_S zid`@!r*FFvfbqysm7m@8?V)?C9GKFSdNE_o7l`XWlp>M^$vXG`0%H>tX3PD&@xx6G z-cGNuSIl#mwv!A|`w4KC`@+s~mxr%xs>XL`R`s=J8-C=T2&EnNx(f*n$*?*_g5ZF5 zG42HW^0VZN;1B#Z+jA1ge)jxZeG$il1^Ht0TaBXY#D-{5i;-ga$7JbwzN!MQX9mE9 zySz+ht%3*qB!j~>a>S_VzcREf*i(k_=!+_hTNSD#0i%YpJ3i;jQ41jmKLKiR;J^hp zlvOU Ogt9AgXW+ ^dw6*k_ARP BK~_?Aeuw4JRcX~be+d{x~G&B$5AZW;< fTjaO2Q}I4$!krV#Ls3x~ z&TkP1FGRsd-M60@6r2uXo5&QD3AAv?EaNMwws7k|V2R2Yp+ZMq{grD+38+&M9C9RR zn3>lX2N#YqGV)x)DoOM$b{dL ( zZafc9!}3Wia}@yGASpgYS+@>ox-6k+LG#&08DMpR67`Qw?)TS)0teJEbhCvp04Io( zfzYZu$TbXZB=0S0GsYD|95TH0?SE(tN09q979VxtfEk89w^diO)(RmrOu8pWe3L14 zT;!=75gKex-Y$nl;d(Vv7b{q(_kAv!w+AdLmL1q)dE4K=+j*i1zINi@pN1ZrB8%Nl zl%%G}Y(~BSLuyF2=R)$s{Z_vV(g7jQ4;uccYOJX zRW4w_!EjB1@F8R-Sg9Ci2^k)Y{m~qrKcW;#^tbMt4+)c-VhblHPe-hRF^0s*{}fy% zP)CSf2czq?*m`*6&sLN8Tizt^3VcgD2ieAYiQU@4!05u{Z7icHAz3-E+)kYFUP>a? z)I_RAT>lCWuwFkp6cY;Bmc}B8b)sLTK)PIaYdrcSsK$Vn^hst2HZK7x_#*J52M*;A zF0h)^QqPeSJEG4nJNO@ceTr*t%mxQ?GOs#d?ZcIELnN#O#T?e7Awj>PIi5Yq`i*o_ z<2rq^Je#7<;6qcfu4(mV11xHU_NImiY;$8@R=jg_kjM)TT;W;C65v~p#P&>?GYv;_ z79`zS7J^^ngP$!DHNn9HW4a1ukqy=~hki@ymk=l}L1cs*t2t32?g~6RHLTT0ETN#G z;iqKE8`_P2&};uXZ237+x^^}@n-Qnzs)U&ahBE2BAwk@}@+e(wG-(-)X4B8 Gw4mF9J`p= z#8030dKdf5(qkql!TP_u*_fZ9>j_m-R)zY$Y(y>Il-Gy6v8tHLOtS>-%RQd%lz*Z} zS2@BeiIvIlxvh*2uq;?8&eqStO6PFb62qvdVT<%9vEMd^U`4-y7nL(nP2;mcCd09@ zo`)my^NR&fdv&!@r9VT|t$B}wy^~M=6XHwXS z(hEc4U6>P3K!GReLtcV;Kx-HN@(Nr`H0tO|BzT4XmVr6DtL7{|#W;S0Op-Bj{fG zQ10zKZfGj^-+30mL3NBB#Y1?cDQLjb5&tOSd%8q+4^lZmj7eJINz~cnP){?>U^RKb z&LY0(Yc6@ C)1mT~JDI6S#5uajKbjmO=R77PKHcHH-+G09N B?FN(rTLP%6NQ3Y z`;!JrS0MxB4RR}lDdaxDBEoOLXk9xoF4lB(M~;mggUdO8cn{nv31a%*KB5HB^p82u z{-(&q^tvK^p9kC7KI*mjg7a4rHHR3f@eT<9QS1pP1JoI{J+dqmF(&b+5ExgQjd4ht zza0oE5zgj%z41Zd=)T#*(1UM$Vwf}jNorW%PP}Adyh*=oP5bh*Q?$UQGd^2@0 h|FtE-XU?x&jWv(>ez`swt;QBtM8ot@D3zOEnGd9QDt7+2(&AHY@} zmIO=Qyg8PjMYdyZYIhSqL6+>^Jg)O;Ua{z=!K$@k+AjMk!dZQHL#a@grAT9`zhByc z*yUi}E6T^k=1+20l`J}`&-G?R;&9*N-n{#yNUI^!r=m9R$+bJ-i@w~KC#xbWy6FCg zFj(%SS5@QJ%jL&E8ac1mQame%HYAhyg@rp(dFE-6+eM!f3v*L=8hPgDgI`l)Vb6x^ zn2j))SH8lmITP))q=sv!{%k^~0xFAb4a$ZfJyZIEU9iZ@(M){UBr$ZxibS4%vAtEk z{fQ90Wk24s*RIrKEiaX`o)x`g>OAM{JQoc(HYd+YWoWyeAslq&k6lBJz4^=rI%EOd zJQ0BczQFgP{D4D5+zY<#;D V3vl*z(5b`G$J3Hx(CudGrSzED;DwNvkDKoR2R(?>hFD-2=Yw+ytS7J>J zJm}V#)xa*C_C0-=U2s|hAY^5w`TiMJWjk5Ykh6y1`V^6lv(K00A0K9#-Oqe?R!6UD zRb*H6j9IitA-Op;A@=qu0h5vn` GFfwSjUN%%N&(ZH?Z$&Zgwcf#UHIEGK! zuQ!#ijc==+3=?MpAI}%*MIWpCgl4KPsBFFj~9wi zrGmpNV#2BLUXFvEkXmV?B6o}L;~cSKx*u@a6{364+wZFD-6x0JKA!(-wN6Ra4d3qz z4QFrjauiHbJNAl(&PlNxozrVx>nj73?)O#q&L)N_F*Y}NJw42_0T=P36LX$-;vm)7 zgM2Qql6d{G(+?xsuLmPxS2_2& _c2XgdL%;ku0I`B1RF^|stk|ruHq6D7ZuhB0W z@}K2(o`pX3qlFz72VI@LsnX`yZ#8@&4Ys^HS-LBx7k$~J7Y#i4HB59!5aUQqEaSIv z4;>LlT4z|aRK`t|ZrnWho;{pW8LG3ex>8EYFXeO&AA{~Y8Mn?MI$YxEFEg<3b}5`0 z7AP5LF*9mJXVQlBH_=(vewzv4nBYeCJ+Y~x7>^0Q67yL{CdyBsJqdgjue<&d6j-@X zewUFlIp3cW%y*$SOBB)-HP4c`sk4QZOojWMiHALbZ(vS;*dfq$!K{4# NfY!$rFX0yV0)wz;->ulfZVo#*zXN?@c z%WnA7M$cZlMNmwA{hag5s 3BKIo9QOcsWxenVbSV?_2YuJwpBkqn~#s5oV~6tyj@nlZb2#vU(4%Jb+J$qc{DBB zdr%c*YzRK#5r#hsdx;7 b7s`fHppe{3y@)ajVms^>VHmMxT}sc)BIGZY6PmzEohEKE~9p=777IN!{(AY%wGY zxO}3L;K#yOBtbTgLklXFWA&NC&ifH^$I5c6uW7%=#x4_{CWP*umrpX>SdRnC+Ta<&gMK!tCEZObg?TNZLoaezq^c{{CGyJfsHMC zn>NXMsvGh!QzELRi9M{zQ&gDPU`fln6BBB%Q)bB5>ao#v{VV2XtkzMvR#`)>FEpLe z-qU55Fx=cmIK*b9MH?$BfVQ*yM4HR8lId`ACavV*7j~S`?Lq5Wz*{dm{p{h*++61~ z y>Z`j7qzg0h8^w+X|JZoJyT5b$-H^1V28?Wp;$k$L5gP+ z-<@cCxv#(-(ei~z2rA?$GvT_rV|}Iz9mxmtlS_d3jw<^QT)Y;wa-Ibp&bT=(r2weZJ66k!2NuRh6I^ n{FewrEgbp3eQnWYFzS6n4Gd5}itZdHeY>C;ntdRh{=4bP-v4PMYg=VQdyo zB4_(4tb#y+1qbKD$HI7ft;TAn)3oeG_XDXwJDEKg;IpSHF~B5Q0WB5pJG&R@+lf>I z;9zy$$!@hs^e$iY=}I){x{Hzk^_BoZ{><+{Lp9Lu=5~bqwKM3vfGxn*JRp$+urg?} z^|Zz_5ZAF^?cI4MOvK~k>^tD<*jr8Hr>{Z}rW!(FYwhC@AU&?anxs_OlO|(8aU? z^{Mf=z7Ni)4;C0gXusQ(6**r+|E;6{iC2>3cM9=oIUvDleehK~H-qUQ#S?{_+De}H zko~^nX_Uwt@gadQp}IYppzCsr<9zj>(!X}+$_y2;H{TXXPvf0R>fliE!DcX6>5d?o z^mE6pIWTw>D=4eAku*$U^XXTvs6Nr{?e#@#UTw$VupjO@>+52~T8?sL;;iP`+03Z5 zzFw*2A7bJa^neY?XxX@>7M4g7iBxl)VDs6iA5;j<2I3pvQYJI!P);~=o{G?O1a>{@ zK9o09dp$j;;oB$8Bv3<2TU~>y)oeaA3XaQAQ5Hi*C`UILv6-uXm_77sZgYF>**aat z)|hSHs;eM=yMHNSnuFVf!ROp?>6IPSh%$FLpi^pHsdG`IKO7YGE9oTbeCQ9lQ8a%1 zvYwgA+#?;i%|n|1i6tv{9IYnp_=(Ja>jJ&wtl!K7g$Cjj6 zp>P&dQ-zUM7;1fetUgP?%=5i%6SvcuuqTm=O~3NT?zjNPv@D^Q<61V6CnX7qVGlZ8 zhA}o)ZUuYN9k;qTv>X-M_wx11eJQL43;J_$&xz>5AGcD0WG{~ckIN;tx~+xQ>vw(d zUS~^8=LW61@UjoP);X+><)=%vL(vN1RdyWIn5>jP3%;B mSu`1x+x9 &ElXRD;b5}dx1jF zDFh;zbtTYp)0Ht}P~CJKLn?Ec-ja_BoaV#pW|c9MHk`$~J>_>X@om)$%WxpXV_~m4 zaeDRPWzT(Tt|o~yEV`@q?3D4D8V8;4B7DF6$*Sfu{O 9Y} zRmkM}=Ab1|JCBu$XXEhKS0kpUE|v8Q)Qamn8M^N1o6&LqBZOdSm>FfU>#7K=1x%k% zQ$H=}gfr1Mhz8xP{ZR06dn8F%5k5}j+Gi8J^Vz!dPH@9-ADO>&&E>kTs~ZNST-ICh zO~p`G*SOR7qHpshZat{2)~j8w)cz#4FE6aWAgmx@sYjC|qrmqeq=`2t<^7WAYiHn7 z^DJBETRyotwygmp&_@8_uw2E}FBubJIn+3HvvxtFRbRyv&j=^n9Q53!t1Q!TxJiXF zNuuC*f2`oj-!J97@gwKDwX0wwkTfpYRYS2|P@SYRSAAXBYueUvuWd!@w@u!E=76f) z0WS_YHN%;Re(#7zaCGRJc0nF=p&AWdaq5-ULW~lc Q?g5S1Ku^ zU}!Ta{r1-D{4)O}`n nd1bQJLM-_^^4(J (5TU 4Tmj|Jk)&lLwwG1nvdB%~?p$S`8$?K*~sf zBu)m7+)hTKoo!rir?TskL 9(wXWL-Ib?e6>J|HCLWEo!HqA zyn4HzTYtJtA^*4_>5reh$1Z<(;$cdaPlplbD{YFa6~-T(m#)M`tjy@RvwKFV0y0GU zf+E Zl5+=V&E8&5j3j)%~jn z(bs6vR+rt99}MB93N@jQ+Pr#afm@qJfm>H~`5csJRIDfDC>xO9q=R$AqL4RK`GM5b zv5h3!yu^;<$xS%)P2;gf&OP$O1`|_)qAwb6nQ%Zf@@g8rhj*0S&cDa<13y7MPF}1< zg-o5u1k@S?USA9 Vb-+3XrMzchJpB#D?WkLL?hU$!IboK;zB?YBr&eTn9h8){;J%5>|O!v 6G2u-kzTzO!s_=$^(B_F{OuJ R$GQ-k(&VkxpT>bQ_Hty1KNr*E=5jB0g@I-skS` z&1xP7l$?8%Zb;Gg6OG|DT^@rk364EV*uV(P;Di6c1-=Bxz@!(63V ?LxWi{|fuoMs*Vef-QW%QlNa zwBdEJc4sv>nyEBGQa*1E#`Zex9k#p+kJ2d|8>ofy53|87R{hNFw|ulj;$Y_{V(J>u zG0K-vN6YiQr`uG_gT$OlWhY0%3Q1rQhJ{{7zNyX+${sO={%$b;=o&4fjYcO#Sl)@X z3MlDzJ?ZJ|>${m2q%Km4r{Ej;RX2a)1PEv!-=$~sVry@nmBkQNMPF5~YHM}LXuSFy zrbP<_pC_1 #$-b*wuS~|DT =V7!s_p)!XNNYa4=9{=T5k*I(aG%EN`59BEZ z!*_&8{CeKrp6nZDvAMzjdG@)^${0K!Fk4@5FbuL8)@Z4*IE^h*#Z*uwJMg)Gim zTW#&>OQs68bPqg=d4^f%d}e0mAQoodB18S=tZsU=WdBMg#SAa6n3%Nsb@@od!0`x3 zAc8Wozr`TZn9>B-l|AC1xBt)tI-09gPC- `)r)dzr38IUA z2x-^tq;5E;#r>`g@elJi<_Px5na(!Af=xRAf++JN$c)~o(k6woT326R>nyRtmg+&J zz;?yV-74exIoRRu`aruXdCIKxr{6bntY`~zrkU&gBf$a+Ry dASM^T+rU4vq?I1nJ%ec z^=KqG#u6An`nFZA5wgp2A7cvm6bk-t_{o7J$RPTwKYq6L{B+%p3$Z3{ZC|zN;*0oF z1zfS8-XOYq>UT*4Qf)49o4KiP?%rfug}w#1fPHLB^fWL&^>ZCr17K^Q<_M*D+>Sd^ zd#SND`f9!n4 U3o^{gsh_0wTYhQ$4PWe>>>m6Fn9y1fSCZqxn- zjm?*2W+Cqfku7MHkRQL#?Uzp* J#!s&X;dVV*qUUwkianj97GJJa zl;*^k<<7Ok(lX=+^5?nz9yygIkTQA1#i*-5Ij4HlUcT67#d71=j(5-&AfN0opEj`S zXb#YDY?T{k{@F|VMUHw^=VD;ixUHaW;Gkm>SfSjA@J)>sFp*D|-V?yQ>z-Wj3|Bpl zCYT4Ht>+D1=j?5VmWY)rn(lJCcYS}eqsCC1U+byTNf|@r@nemBk4KbSXJ!y;CPw0v z4*>MFFylraa0k~GpLaZN`YPda=8OXH(J2iV?zWy@p70z5^e~QDz}Ej0YwVmob@5;j z?}}n{_K4M<2p{N?;NPL)o?jRaJ?QgF5dYF{uyiUobv{DEI#I%?!y*Kqmm>j$9d$c# zcU#K5+HBIQ`>780U&VH3b|<<9KNAV#j%0{2>%kPi@@bR|uqY)GJAQ#g3qb5{$k{>+ zqsH8Spe{hUl%R0OO@D8><;%`f2>f@yfJ@Bbc{A|=m!CcW*%tsybO7OSUo`y99u!`k z0q#|yYI%8SSohoWa?tl*?K(?8@UD}ORzn%1A@5$;+<$rHlMwJYN$sxawwv{MF3CX@ zo8u%~XqQtkTx1KlU2Q+FY4c{#%3+{e8vRzH0lb%>Jz3w65 z{r3L-QGdJ5(eXkwF3n}{L_MZjx-7tEpalerg?Y$q?AjlRQjQ0 zcV)lj&c2a{a|eyPL`vl|npdL&|DvVsVcxiIql~uGWukg2#wnmi50{@pDv1K%?K$+Q z7tx_kp)I_5D*ls~=MMH&21WYUao1!pgRzh5Adq_W;|Y@~NA3%+EjYW4*8|NSteoEM zlcO}TXI(mfkNoD3A|&Y%2(+1JLFASOqv|A}x0-N@vuv_wMNb**b3wt7>g0kzPXahS zjJoZXyb7Z79H)1p4s=O!3wTjpjL6GZj^skGaWQ(GF5ce8vMS$OATYp$A>7F$|JKxL z4VxQX=F#G-WXjwR;?-A4 I(ow21 zb=g0kTzWIn{@Y&l#Pw(I+kxkwXO1~LLG%1aR5Q#n$rj`Z!$c2{6Ob`&t|o5AgNK=i zNkH&}A*DvQU66yL)58<<52`3cYxpcj+=yB{pHG6mXV13H6d?KD-LLxp)_E3WU^_7A zx>S-6D%U <7-3)gOz`WN?)YCAWBSank z(!dk5y@CW_?ZF}KfgrH1@4*1T>-y~y5N>edCNq-(iL-!_> d7mVv~ui$J5Ie1tT3+L?$A-8C_MAAwJSY zrEcrShAqvB&bl>uHj~2i#ilPfjzW1KS6fa>h`+}=HQtu@)};l@!%%`&7B$&;>HYDU*8EYbKPJ-m85hI) z`1)-qVR_|Z0>Jt|%0Eb9Ajw3>ddD<^`Hbp6b8V^r++Bi8aQv0~CmC}h%M5n>CD#NZ z4R9NLWzS{MgP?tQA7pmS%kP%z6qIF3I8NF$&gRCNp|`@3pbqsX;wXR|jsjd)7BT&g zjNq?9${yr$g#|Q1!K~%*T_H424eyp|rzwayfLS28KsEnL9u{SinIh1*?B>ja1wye7 z#?imj^>$1hwzVM}MRr@K1S)#$f>OnzkM&1UIedY1zNwbncM1}mtE;xVIYt5f_mB6- zPy6_AG+`A7enf;UVBVu0k;t`b9 q!|&flXe6Z8n@-&O3mJFOlVyR&D^Bv?FvQ1KZ01-I)c3{R_-u6Bz3##* z$Knm0I^>2Fpqc;db7R2(FzGP7(S-R(9kSbzna{Zu+N*$bJ%3z&l(3GDq)6Oj`EpMU zPK`z3|6v8PZJ67;F&ND7uqv@jU7U_j48Q@5jR04Mga4%pjL^XTE0Otlb&)p>WM@zw zG*d_#SWd*-0l^OfEoVXn0!g6hs!Pt?OTk)?uwS~mB;6jl*mPG=cW<*WvM4qM5eC@i z+V^9V8MN87g$Z=X+1)g5@HZ<(A`r{PgVs(58G8anzDTtjlwC%^*I@qCz8*Y)KFg^Q z@;3gsBiOn}hFdQ;0wVPE6e4~#I!rDi!E~yq%I{<(x?;IJQvT}!isU?qemvO%+2CQn z%$1Pe>G4HTh+&L1^9))MhG;|j1DzxQLwa{`<>W^-M|$^b_p4p^ if)bsA4mE_Q!J}E<1sisgI@B{8afMg zgeEJ2vRCY(q~T^2jZME~V-r^gDa&o>+4zT3tQODb?$ygwMvf2)5L`*p xkO=YY1eq-z{+Y&yvVhl7#GyEKo_TcTc=?(`=i97zo_}zdCMUEs~qKYIL2W ziY+V=hDXBpc?m1@hR(4bSCKP$!@TTE#)1O}4QKKfU*wcvfX)ZWnr2q*xAPx+)2|y$ zuXDby-M+5QHhXi8_!&uS)j^%f#=b|MTOXYtw>f@5r%U8X8 xucbIZW_twL#*jv{imJ23$$xV7tISTuj`b$m3aaz`V}Vpu zR7ixp1QY8x=MVGHCxUkHgxigNL>OYX%Cz=I-1sCda?BkOVm{8s_-26B{~3;UrFs 5OcqO~#%zQ`u<4OmN%6G>7SHxWErzkq{x1Rvz}|LUeS;m6q7@ooS)diL6k+B$XbnRy z=UasNP@Mu4esjMRsLck%38CY_suKDPC&wMEeR08bvq5{ a-Th^2+S5cT9)o5_x>Runh*R?0%{lqXfpJH}#HxG3 z;&VreZI 80Be vw*!LWF7u*deo{?7_1PRk}X4#X}~>?1NHKG=fC3LWB?G= z-k=o!4FaAmt~!@)y5N+Uq-NI7&fpRGyL(Ujg%x74VBN9;ii;z19tR|~s;I))6|34e z +zq*dzpd`(68)-^5hvAwi3wb?$06RR Jy>kB(A z(PgcX+pN_T)VhAo?n^Fycq_kYBEl%Xe$Axmv$0&vpf_qLcJXwc%pg6}H}m%1oJKg8 z<#@_5V=iLZuY_&{i|Tjg^4BU-J-hppp64EmNOVQj{9XTWQN;hB4U&Nh=!y@ElLU`o z8zcHQgGLXhe$Vz3X7O1b^0^NjkmSR}I`MdCwsOYKhk|ole&Rm6C*ry4GcGdZI&-oH zXgLTi?&G;P_uajT5pvrf?0!WLlX!L?_y^cT=zpplaA4Ws&`Y+!*NYB* g|qdd?LC=ChB`Z`Uw>6tTjhXl}54I3UAD z*xoPSR^)m}QMMIHdaZ$SV8AFjtR8Uq#a1@iM7ChIvUAZT#dWGvZE !eG)bm-t$R>cP`J;*V^bN?aDFL9JB&E?r*-RPL3 zC_QfokaN(?l#jxok8<5$eJMz09+o<9Pc?{A^+q4Vm68xoE{d|Bgx@d_RwiC zHd-=qdRa&TIHnkP79b1Ze7b14w^vOpCaPew>GDbTm!>E>^v_vYNMkA3Ul?3+>V7 zJg4{yJQ(M6S-E8MF&ZFbTCx|xE|)?b>!lX jXOV6K#0HexPkNA%s2Nid>6>|#Ggl_32vfUV zFZER7e-hAOBN~qF{MA7|*l=or6qj-T0F34?W4kE |2<{7OwZd6 zOp|54gw1!_#eEG|j0V!x4NWy3!+Ko)lc*Qt;H<3}cvRA)Sx`hqy~F|0D`q1g1aFWZ zrtx^lcw`@UUHC^BctT}F1g={lP`&%5@2G04>8|?6@bI#&(<=aaUKg46ZDTYrf|jB) zEPihAcnVXbJcorKe<4qd3^ym2p3xR(#&2QH dOL%wTl*>?xNPisQ2<6M!8j2!6D>8ta8y3q0c1S)i+ zw_|k4ez7?>qD;LF3lY6-9pc7SiGLH+zWlff2}(NbdJ2mAp;*i2Y22us_8i|{hou9v zd-c7r{IGuqqARp- I=3Kk(SE7-ShQI)I~(WHuIqJM5iX4kFFVuw+jrC;J$VuJ z`4u8$uX)MzTln119?MBEu)kctNo^AJy+XU2;w*OL%LC gF&-P~T zEw|X;slSz%SGg>^!htTE^cl-2*J|m7DEp^(sHfa!En8xyHZO3R*CAZJvyeAt#T;O} z%2-}7ffxpw1PuVCZzp^Bt;f;(f;--F`DR#xs?(!WP|#(LocJzuIHz?XOz(M2W15K} zuacQ7kC-dJ%G*($J+Hy~XhrBlVr&VPGe3_m=M5ZQzl@(;59Bnr*lv|j?oMd7 ;{9ylW5;p)$AN%|9T@6+we{hByJS;PfNqiA zW5|&qF9QveUEwK>Tq>VHY9{Oult>BuVhMP|i!1DoZ1BkcKskf#rhzf&y9Us8nKYK) zMRcy@t;$s|X@sApB`_5QlmjXd5$AR&CISIw!Ov2Hq{+17SX$j>kH&|HB3Q;Ps6)#4 z=SoSEeBr!s`ClJ695MFrK&&MaXg%7`QdPB %3TLp6ea!?&+q_pnoaw`8-weCZdynMa(o3Pa&Zbhq~`S%ZgFv}Hv zNIOm5KF9;tZ-Yf1YDq7abZ0=cGE3{B>!I@@5M5D6TU#4bFn%#u=8KZC{s>bSQ_ {Yr_@5!zU?@YsqQ4;73p8AQ#ay1H*1|)F j>3!vS=B7maoaI z0J8u7N_u}1X;vR(X0NOalV( #rAKC}M7-KzI=-<%pw2<^OKC+p4-)UgqU@sW#`a=Km2`NRs&Izq z=^J9oTf{=QR~!oKU-*rON#^rzFgFAT*w3K*l=Ca3fjeN4UbwMb%@3UO<;yKcp{(g2 z6-jXA(if@ *vg^^2G8qc|KLmv&DMRtDqhUj? zC<<$~0p=2t4`*d osErascPcODMXDBlDUMAb`83;5%BVZ(mhFsZW{?#H09vb}gku%0st=Tro zTBcyS=QLd09B{Im !*gNoUD2BPgTw;b8IWf}9|{-HCI_a4uf zA>AUCFiVOjN3((nG(;_{9~K~J--5t1ntYDpyrB`Cs0UwU*@)+=Yt!;s`>w>f(v>4> zM5euGzyyKj{yKzrLPZwUmPcYaUpLIS+5FID 8R~UVzHTQ~ z`%}pt%@YEZlA}02(O K@)s^Ho!RqZm9srT>XnmR`6 zq|jC6rNL)Lc^OX>{>oEp8QV@AF05s@oss~nxd Swi
yD5~#%K$XBp60f z@3EPf=VCzhp6VujT|~^k$aNW>YS@U(@MdN3-g)~aYtY?ox(9h}Ty_O`#BWh;KTk)J zoiP-rU$bn*QOKY)rUMNKQ31`yrs4PWt1sM~ZQS@vkSnMFJ`>>Z;-_a^k#&;OACKX> zLT%-DdlgFr-gA?lhpM+2AZ}964QU8(I9%QQ7dqhEUpUXE@dGDbrFL4_hq-q;q{$*T z1Y(gB=M1hW1;s`_`93#hP8Qz!uB_sDe6FP*hIql`QK+Y_15zN>@ZvA5@`Tf!*Wu&& zG+&sZ+H22O&f8rtwRMz&cF~i8k-; OFU;gjK{lFpF9|?Lh)9|yVX@3lZz&m0x=2XH|F9dD4t8uPQ`)n}6`D=t6`8*5K z7{wJ`H@}xV4-abG7n7^j8EB_Zh~9r!YQ^9pXYoxH)1Y}CLi!uye)gHN3E z=XF6}$o|Y`Q{7RY`u&g11ASiv0^wYyx{W&&cXVn|H~YLyu4!L4W6;#Ee?ng$i7USg z5*$VP*FVu YaK2S>>Vm(-eDB22o80{5H6)x xK$ z2sJQ#y81Eo=cvml2$sQ#S%vTHL@`W!m#2Cj2Lfg9Mewgg$$Fo~$akj{6|6@+L~7bn zWn!>5RA_xetx^TY(9qDf+dO+z0e3g&wG7I6V=SD9fieV*g;I5VBeEt$CEf_~{Uh^j znHyknvsQbtT(~EoJ41 QP+SEqs3*zgV1?Os9P!tr`4tQAjEWkki_Cs?D(N zAn-irY4=FceZcsFoMB`Y;?ONIqg90By}r^XQrsO3Q=75gl+)t<5{8P}{je*93qQ2h zRzPu99M&JV8baraCJB!z>EN~OTzd*Ob@|%LG)zUcE*lXsX=Y`Ldy$=`kiqBW1hN;V zQH4p!9(O|v@w{v`x7-U3JFz%?jI&HL(bIA_%4Ah~#HxlE35b*@=3uR+; Fl^|r$p=~BRVeLJJX1X*r`L0liz=MgXkK83N;~d-MkLUmiJBhr z(G@e>fgnkrnuM#aC9;#s@Po+l^d!rL%l>SGl LH*tFzpy%pO-xUVnTZz?2ntj1iiXi?v)CA8z#o!t zOhd>R03-<_3S{tGiWUkX!QHzN2{;^DsmfCAEbr@=x! S2o4@6WMN6zcfwBU7J%sMqTIXR*MC{t7vUp$!DWONSa z#SlC^ydxP$1LSgFc7T#r-7A2&oRvp2L6T{Bn`JCOLIl7$8K+4|B*5%}_V)>1K)t)K zKq>12vvt2$wQ@0-i?&P$+8`MyE|IcDr1h-o? |r$lPLg2MtG>86 zRH+m$V%I*{7@_g9#jOu6Y$nbhRJRHF?RJ5FziYWkyyo>kyM9wyc?cJ9Con*oLNMcy zn7^eb=)cEjE1`1*_{a!dBKv-P{I_voT>1Z$*2RGxh`6JXQAV8;Y$;T5A@6#S4e(i$ zj0?!AGo9u%x}O)Bd``xVjrYLS+_Uh|hh~@Sd|X^s2r0j~jT;Dq;bjAqeWd_yi(wxD zgO7d@nR9<=33}j=cc*uxF!07O{pjR-EI$(gzz_hWiNnvI7dc4$q#BNA^FAd4+MbHM zwXvjAAzjQ=CmS-tS~d9eN%c2O&u8_%H?59F(!Um(2Wia1_c*T#gaPSKc`Cx{MYYM0 z^k?jBdvF7F9l|PUpK0 HjjggSwx >i|~*6rH?kOx%(wc@g+*%ZVVC;gMbQE5;aCiGNBOPQjCLD8VzP2T)&ME!)&!A z=lu^}zhhrke?`dE;Ne0bK~;5_*mHxnB-883G=(@dry%$t`#O!!}(Lswunh37s;3 zdjs5b|2vbYdLH~n%zh6<>w<>rAU7GR@c#tKa_3Uv9{YuoWPl*^Q|D=P~5na@{L;+I?VBW>No+^pU%@0afD#hS}D*Xe)M*=}>v-F+|+g(SN; zT_%}0afV2(4SP_RuN60T?Viw%+`oW049^>bJYSIqlT@F7C3PI(N;wYqutF5*Sl1~G zxV$W6i*WCZOvx7c^z;3>9^>kCP!5b=!veNZv?O|#W)4W;k^7y6Y%&j_g G z=CDsjW0!ltPTS04j~27%aWEiy &p}~?a45SD?Mwa?UM;U0~cCqmK zuJxD%p_OzbtYA&y1?%eVYVvxv^}2+~wPpJJ4@NfiA6(nP;d-~rN%zHC=a~l4zY*gm z+nN}8K;f|q-((V%*>?5E=%?Wp{o|46)V_?3dS3@EcesEZ>XqIn!(YUmARsy4QZa(o zmz6=ULY5FR*;_}dS=fk}LrZYB#(O=d`+{k!iH5A-VWx`$QT{AF`sX24e6i@Tw5LVu zsx>Yo$?{aC%>q8Q{_AXWj;CIGql(=^dX+7KWJ%_)xrp~G H)ql+og% zG8~voEQ-(jWse1kYbgE%oDB*hfK!BQ{sYp6(qp)Sm_o!xee*TN2$s_t6*BUCmMQEy z<4j5EQ=fl;2p-}Qqayn&?6$wW)wmaL=f%Zg*1M9z9M0WT3%uHGJF|PgPU-$NJhd6C zXSeZdqAd#FxXP~WCOXFJc8~aDLI{msgPY>3SM1!3FDI#1b&X~t*#A4rRUU>!^q_g- zD$9{KBs{F!1#_W%D+K4-;`8LC+dTfCr)W$nvW+JHg)Kz#-wQH_DSHRS*3Jb<>gGkO zmK+7#etSRa%_eUrCZL6`^5B`gYUJj%5liUuDLz}buFOiZTuv7?^fxb@+6*j-Hrnll*|k)Yqs7G$2Uq9%;dhXPH6XsvkMl%4vb-%DtCvbo z=AD=JGf(B;OP?#t$1EHPRzb!pLLMe$?LktdQGlGZJf#C6QPC-7&HKDY&cnSs=8(kN zR!BXC%AzI1Wwy-})50nFo^e8ComiMHn(Bo@RE<`%&)YKN+p{>fH%@fV!}d5lI*{4Q z$&0|{@V`kDO~|JxaNeNN`J5SaQ1rvL8$!VeL$X24>9||r+4509NA>x Jwe44goji*`HvSZ^4^k=l;k2JXB{PIemnWTdf&Crp_2&xdqCrd zqXCS6BrQ1#0D1Jgysh3tOa0Ve3Bqv4r)acrX_0(JyoX(Iwvq!`0^~3ZWrXG~wVp1W zh7=tn)d6D(Qa~;B&9*^7Skj?niQ!bxm+d^=p-pZ1r;Ee$#@0>Gz5~o)!5$SuXntn_ zR#ui?9gS6gpqR?Vf&`Uqhv%?!^B)m{NO769SS#qAiPz4)IB^OxR{E!DqSTD6tooz4 zRtW}FSwNo@<@QLy1rH!82wRJ^Xr4`rdGmcku;j` KRGN=E)@?DZlwW}`^lJ=^1c9< z=9_0~&vGzLOj*G1?d6lAp4XS|wk5LZz@W~mlNWLna4lW5!2Rd8u+`Vn7Q+=95i62f zvq2Nr9*7+%Xo=qW*E_{Lm3H)#IAW?R!;Lo=Wiv@KG$z0gL-GovpHKvX {O9Ly>C-M_l~*N(F%TK?6Cz}T(#cVx6mm=-gC zL}^f=2@$vn#QPPV#Cw+JuShD^*iYQ;oq{G3g0h9apO<<2e$vn99pK$e%vE1yOIqWa z34^-YGqv=6UQvO7;dprn1qdTUh^ZpC=lbcc?E+$jvkr3O6*a{?Hgws*zk#xzpX^wD zh H@M-bfJ;3K4R{FH-pdH>lYGmvW2gBu(sPG279j&RV_*slf I?DqJ^@|k>jg$AgF+4n(~ng9pNMNIK#&ABq7YF0r|~bCmQ4LUs#u^cmk8&bal`sN zKvRh;yIadp;L4oS)`<{V*CT;Ode_9%^s|EX$=kEPmR#wGcy|Hjb-8A7gF<**c)6&H zJd9e&Nm4W!5ddhC6_8RQlo~?x-jc!3pAeE9B$$ucX6LvTMj6NNmBOPS!?Ieo)6`21 zp^lD}m)KOWG1<5St=jfG8d27~{`&wBfhWn#ryG#0^CRfoH-?P)d1*P0q7lt%WfSiP zd?W3VPvO@W^tC_*I{>pO&$QxSt3WWucCJ{-^i&n$(0Et9^_O)%W=tq@IH~;aulMlT z4}T(<$o3kGBVkS{Rj?JP_~GxFBI6al*KiR`6K7@O5oY}A5N1v;M9r-uem@Su_I~`< zLX39f`o(ShL4YYWb00wo&$Z9CdGR7TN`8VcH78r|=8;Vyq`v}+16m*@s)kHg&Ea>c zHHt3W)0+>5R-=LD*V@v1>@9R&ui5HfEFVTQhyP#e4S|JBgaz7vn?8Daz8s;zmz=&> zTh&d&AOj0fzX;BgBw0P1Y}qr+i&dqi_X(s>oO0fc^<8(p@wskvo@ML8-@qI1&ELhb z5Ceq;z%?F-?`2$gQFY1%*s&7naIO6Lfz;ki&MlW#!}CbP138W`VH@`QaBrf6QiXwv z1j2A^3G)%@&b;SolRpm49|k9riE(`R9w_3fB`-5Fy7#ID7RoHLa16gq?Ae6nXin}` za@!TbCo(`VYqz|ukq*hlglabX`ydbL%;c;X0uvs8^r(e0o7F$8Y%|+^QMkm?ra@0*SAL!1r~3fB(~5 zb!X@E^5Z-yNssl9IdLRbf0}uG6BSIwz+W{6;0t#EkM>AwY-bcghh>Yl(uDTCVmkpY zAn$*Y $a^=#e}X~gu(wFZ-Y+|C8HGOI3-kt!e6VmN*5*elu#%e zu8oCCmkFoIMVI{(AAuYhW@F+i1Wo&deGG3C%qZ5khU{vzNYhpVf*i1AY8cKj0`GyV zo(}}6S*DH80~62QU!hN{n~jGF&zUJE7Lk%<+8I;G5=h68Ru^{Iz7Q`#B+fDvQ^Frk zNTj86`v1NK^5c8!`I;&cLSo1upFo=Ez<*k+ra8tiq%0fJ;#@-K#A VwE-~9sD;;jNJYF?)&zjrlCtX _YwBN_QLN?;}h~9Sts=I zKPBLoj08)6x`0bPlwx*|d;B1Vt+&+i3By!p&z+9Uk2 o@Gjh_{hRwrWz5P zBz;dc4i1ps+i(8Ze!6^G*&0blr`spxUAreQh2Em}Z$!r(v`B9OWbx;)nN_@1pUKNB z=(?;P*ty^Q2xLw`4b^rkP!6d-^%VTuk@(BPf;L)pW#ieIEvt51=V4UQ{TY4QWB11Q zh^NZsVXUGW6sp`t@f;y5V4}1lbH`Y|cpD&_M7@?W_CO1NplR^f*9_h-Sw!KlbOQaK zj;4*w26NobbI1uaT}=7SN^)NBcUe|5(?=MRoMm)_-vVn)b&%v?xwn?mahil;6hBzx zY1g`Q`jxT!V5+^XEO9~}vm_cCmofa>Akl<^{9x}pzl7dMWT29TrHKbAYKj>OG6Rr1 zPr;&+Q3!3X1G+aA_%5t#6iiz~RK^7m-)OzLxWnhHUT-PWtC7u<7^2SpS84u2Zmj>Y zRs`zwufvO3gv8(X9N+?Q(X4)nVa?W}!}vb-9_H?OZ06B(dTt&W?sEecdG?qNC9NUE z@zaBX4jrcB$v(#uIekx<_E`%NdXr|w3*=QEBA+!ySOwTO0e}PkPJ0UVu+V4F9Beee z7bJUt2!LQ=T z+&cpac;w_;?@L_mRagwgxX<3G<0|ukNCca7y>=_s_5ef_-p3K}2+0X_4f2)Z-W3p- zMz!6v*P?0xQvU@19>Qlca?r6vo!!`JaLwtzrkiU-XI_XBq~^Y2&KO1a+>bl7 {xxl7_+BN2s87bw5vw8>smN|Af)Om8 z8OkobmoB41aRNoFTngBR6cqUjCKYz5w)ivLL)a4qQ>8Pe%3!`!B`faHAH_xFyCFSH z;5&Dl&cS!lM!w{6zSbyBAVNTi1koXaaj=1bBg5#9LK23H)Q|u$rOY8qX@nGBPZPEU zjuSPMC}7S*rftR3f%4q*#>_dw7PV?i^ey=)g+Vgn@zz4NXF=G$D_J;p z1pA)nD16@YsceA<%d%qXW$TY5NtfN!J_b#hke?yHMr)nGXwcg0@nFt>Jjh3FrHLyE zMwF1LWxOl%@eW_=k=*sC2i2kINk%eQ?^Tu9(P4g@ c=K&}2N3#f 2Jw&z{~d1lpONGrCEOC vD3#x3{ayzBzuVMOKO9KLi(PzdtT+93tFjTb zPnqJOJnw0 0h`{iBS}PyS8j+)QoMw+ubV3vr-losO za(@9>cegg>Y D2}bS@f5&NW&v+TUmeObV5ABjadaqLp z?= tPj!eE!4mgX}m`w RXYX?*+d8<8PrKi44&M(AK^^ zs=5-o((ak3H1Y*B(y_mf_!vDXgndr(KU*|)gaOMsj+5qsH=jigJmFArIz-{Ac>u!T zydYYM6J#w6z3t}h{m7VpmI pj+1f^7 z0B)syedqYv9>UdxI}!};d`mzQ)Bj8xIruO%tnVrp4bR>Uwb*p+h?u|{a}u%0g{~>P ziv^k!1JW$?toQ;>oA|2_1&n4sS$Oz5l?}U|@uJw$G2}Suf^Y-0Ar?om8Kr3GZo@DP zoi?OEqBy$vHbGga4nO9CClHihq!$V#15C1tGy~x+s<5-6WykAxs& o_Nm9) zf$M8gG|7%L$ZX=rY^7&Kz3ccvmzSuc%)Q0e1*Ly4^nl-*IZ011vQ*x%6&!F&X>3YV zM49M_(3K7zB5bO-tcdQ>SbkzZ&+Sj87NZt(-Uci59sm4DmX0(6gy-JnvuHR`?7*%u z_N0UwMQ%kKyDmy0*$3%O5=@;#Ve8bkv_PUjDrmp=?s$S=dZdA}(n?n*g+I8FDmYGW zBH*pO7El1f%mST TZ5zr)cWoZokzKdv-=VYv@sJM}sng}@L$Z6&o}zGFp0 z-iAKUH--ej7_`s+|I1TV4m=DE=Ij4s=^EH73)lAE+3jrCWK6bgYqD+Iw(Y5A@?_W4 zWZSl Dx5#Hw_C}3Jx4u!O-P2aMPgEF8vNvYLi zALc1se(sOIYrX|yvwxb(j93FcB~UipS$aH;vVkDdVaI1IRTV@_wpQv?haR0OoPlbL zunF8b{2^V+ZP!W^RiwI4NVEh~|2@duC31H|NNkpf-ptg5;t!fnd8m)Qi_Nd*nd)Wy zAby)*Sgw8P(dFKUX3s6>;|k^vZPO;S@9@}=AVf2rMh%Rm@3$Wyh et z2l>ykrbmBWxP46OM()BD?Okb;8#rsk+c{T*kE6Tka}Zgy3!FO=D!}&RMzTTKm;nPw zO|dv8@^63@%0v)yWn<)I kiT9>0PGZ*?gc RtLGK>NR((>*DU?} CFNsCzcTR6_NtAs?-FKrr(yPIhe-pZ1 z> 58x*LcueS zHDlZ%m5ozLw4z`r1I$>c8n|fbY)s5=$eN*s1g1h4!xRZ*_yMgJm%3h^iWLJ2R QYXmU;f0 >O0kUG>u>i3A$`jF4@oIR~BW-D8 zNL05McP8c9Q(1;pms{Ph-Pitwcr_D*1qkCbVSobWhJlisjWrc$*C3||<6I03jMw9% z+Mbt40 2ws0l#6i$JN++j$Rtbzz%jeE3LGy^`Qgwty>EX;COUPDF8^ z@i$1CX$bZCp#Mj{5=e`1QO94~*h$`|N|x&r0i)ix9UC95Yu>VWA6--TP7@D23Ad*2 z{_kt<7^M_H;&?Z&^7%X^C7&}~6y@zW#NI73l<8 mP7c*kus@uRp;ZR|7)*IY3vlL15%v9p`T;7l(ES{qZiq-(fP3c&N{v z&G!7hXx?7TV%5GmO?g2W*2c|)j6P&p`rv98EPn^NzOerp%plkzon8Yy9r!WFhXd0^ z!9Kzma7Y{?ak8~602b~~Sp!6iR9G;MeKc7Nm8_zCJRh;4p6-Q*Xi3ws3Jsi7-;jco z|Hs{D*J)*Gi85(bpD~T~4ofLC+~4$X+rLBR3iCTsbcBh~8hvEGVj3+()c-}j8A<2n z4Mv8I*T6s)DHN^D9Givsn0|lM`xm_2^Ik(QUoKvP4KfxythI2Uv(BPafg0r{YGzSs9QZsg`6USH=d+2m&uv;NY5VZ1TpNufe$W0*bo5v5X^2yJ znyjL@@tbrleed`C< 9Y%T=u zh55)GVG%DSxg0iDD!EGPWBWt#afGFr|01T^GU%4pGZJG;Me>HgcWbKPS4dI fd zdQ8jO@?3djhjXWe%jUb?(q`)2hX0L*AE^S;Sh3j~L8e-05A1j|7Y#>`Bal7gjpv2L zkjUK*q{+&vSv(9kEoG~>iAeYzfqwXHf95I$yrCNY{RO?44wat3FAM;Ea5#tWMJG9< zdjO#Zqo{8fOsJt@f%3K@WW%CMAv+NSwY@u=*DY9Hn*+4nvWkS%oevk1-Oz;ldcDs3 z5tLZDT=oy|fGIvZ&X@u9G8sbKWCQ2t!@}1K%Y91L#q5deHU^NHPCQ w^VdgxpIq7*AxHe;NIclS zcQja(Y*{mLWcqEF{(`wCGpY)LEc4sLD17oPd^(W(IQVnvP^E&(;zGgEfBKt@n~5|| zkDxGv(G`Ky7rZJteobmeriFc+PKG-H6w>^A6q_t`;L6^rzoH2I{97S(r{n7KM;jj9 zPz|RC5GnmfwRmr#D9>&x(R?XPnui@l4CM`*W&Y#6@i@ f#~*0PlT4dw70Ss@v*t z1YRF|PG8Oe$(x?r)!&siT*kq&b|OzgPAhc+tDG;bTOR9-t(z^6TLR)?8#&Z9qNmIR z)EW72nm=IEL@fVS8EYD8!p|Ihw9bG29Qeaup9B5>Fjkf(gcY;TeR1$yC^$%HEPc0IFn5BUxy=TuZefcr8c!r;)l(Z_j zC8vF*Qo<1VpY477wV6-uM0oEjaERIW?Y4iXmI6A%s_rJsNQch7U&stwkA!t940Ab! z7_oFG1_fv*pqn(*rfNbbex!sE7>k)p;u0zj&}{m=Jtzg97Ebvbt-_kAWwnc&0Z0>r zS;)Mzl0c)0Khh$;1(k-4bzqZ20)i9RkwnM_+mG`--|x9x6xqUe=BA9 *w{~i)#0>^ATJVi zLl8#eJ9wA+`tYoVQ5;9v^Ob2ybD}Vdh6YGUDjOuFflkd@3R^~}0DvW>02Lq^uARVx zLRAr31dsRU=M^cUnv}m}uwj(%=A+jXx-IE!KTmn~-h?v?+*>PYSx_(?1M~=mNE2d_ zq)Nx=zpFyi6G!674>7y{SWl6liAC0vA#GP8!tZz@FZ`>?gBE{7GC|d@Hl)-JA1j zs-u}rEmaL&0YZ9fQwzHvK)xU6-nrS#W=rC__CtyL&Ux5jTNBYQWn8Kje;E;q=ccL{ zEu}}>AxG9f|0Q%437yqSqRocPn4{@$JvIz0agZ}??f>K&VxXB^H|DSa#M;6y0>GV* z7m-mGgGcc&Rx#sTeG7zvi8!PkNDQ@9G?$ky#VU*;ctsQwV}elDrKk3E1WAw1f%ao= z!Gq$2`KzYf$#lgEA`iZ#BRNQj>hGW~KF&i0{Y{h|psMf&ND>aNOc;7&MI9!IEUQSg zK-dNF6VXa+VOWo(8`))*LKIS{oHwj3QT2UAol8HET)uyI`Qy>i@6^`tb!Aw{``-~` z$vTm<{t9sT7e4ngPbhgQp|(X3hmpDjbODy-${WBb49>xZwSK_)rR4Lj1e_RK$Vu4S zO}nnAmZ@6kVmL31CbY;e%aOr7aY9+3?aJzuB3hw(B0r)tz-)VuANH9T@XzF*u!)Xu z56dy_*VCIoZULi$3Riu~92pq3QuyGqU|nUt>kU_-huOrpt-Hd+i8#9*RWK_52*KaQ zUYM&bq4YzX{WSJmBC+?R7E|Z}`Xh7!5~bC=6Hu=GKYotLg0!d%Yo%^o@kO@nJ*K@Z zk;!Oy%Vj^azz8skaP}v@6MI3e1X5_F-q|#S(#tr6iw%wFMglwY^|zb#yc|S3>+W(Bjv@ zQX~{!kZNisfgnM0Tm);hcv)0PJVo~u?yEEe^IXAo?@l=L+Y#4)bWj3HVfxd~L~zAS z9goH;g^i+@IQz8|`~E_`b4}ODh|!Y)<&Y!C7`uz-j=s};TmFBgR=i4UOzcvuF41Gu zgS7uDb8m)8M$4-jzlFt>G|a6I3UJ5^m+_XVLilxDb=Xe(4)g8Y_{t^-#F+Hojd;NO zL;7v6Ao+M4BcjnhA@8t~9UyaU-P8oWK6IR(O7ZWZ#Sd8lZAOX+T*xvP=r=QAWncg_ z7M~r=_t!Gen9Ddij9Ya_f__g7z87k#sl)a{wg07qUUaG>ugF(y=PfWBx~P*ZKRYm= zU=6-4=*Nc%PO|HdA8^0cjCFS5`#+e0pr{L+_R@u(dXAs0H_R>Ulw?e`kH02RJZ&f+ zl$k+=aOlF6osQkI@=pd=6YzLrhG?Qw6&y=nl}9kbbq4){(94 V zoSQ5mJ#62hkQ|tq4`hQ$=F)9iJUSQQG85&cnZXFZZ8L5CSd<{~I;#_M)$wQ;s*94# zj>VRR+Dt`<|BM&IgiMn59MloDd03A#w;Fz8${MNUIi{x{KQ8dy+UoWNDwD|;QC4Lh z>_2ylz?9Nq4m)?Nxbon0Vf9Ea1hLKA_ZLdxQy9LUE$`fM-!DiBT{&BBxSvn1=85!D z?F8i;0nuah3^U^(P_cj8-8MTB}Qmprso zrB!BKEz{?}?V7!BV!@nVrIT0A8PuH1IGqH`p$57mh)0u!ga8V{k ZD4b;TR3Zm=hB4s}eOa7kbc1OO7rB=Ike8r8~w)*@Noh1^Z| z?!{E?utOx#dgVik24r70K0D>qEDf%3A1k&JJj8*Jw;>dW@DleyJRt!$j@uutiWPSo z$NZ1E;3SB-H-1KqxUt*0!z3lGD*oiqgU3U}xau!#8)7hJN #!QfyK7jvm zZdoBgstMo63+h*$de81A>9@Ol?Tu_n?Cfq`kk|G`adohKp79=y$hD4+d22Lt#MuZq z<>lq6<>*mNC)w}Y=}X}_(Oomj{th0Pm8y6)SE-7Sxrl{%;#u^`Z0PC$kot&*cw{fK z!m@rX6c=Qc%obqgi9#S-iAAyB@H6bQ5zXNH;2SR*lEpKt9S`X8QVC8iIorw7crnLt z4l-b1sxH7`euvWlz -hsa3L>{o(8-+3S> zf)4@npT{e1MH4nCuFLRz#x+;nmv6l pxYzDUSvD1e-7XdQ>o=yf>i&= zKtztjm4tAXNvcU}S+Mt!Gr?}HBHDn%Ew=StMl*S)(f8l(npYGLzvs9thg`IsGyOC( zE!$e%E?Zh;ah1$qo3|ZMoI7n&%gY6sQegOAwIAP-ym{tOdf=M`y;u0ocBb8B=DdsT zL;8R1$vdgzMV7vEfWDN1F_$Zbh42epf-3@Nh5U#AgbE5m$E%mepANf^a2$fff{=S< zeKcJ3lp4Z0WF0KmXN1@3@-(!ywsr{kRIvr?G4(rr9in--@o2&uN;SB*majZAa%-~P zZ7aT1NJ gB%e(c zj3 d6k$rCIp*#QX51^7G-Qz7qJvL9 zEfakiV8ag6{v4?*N&E+C5pKUUzd+pyNmB*3#+QW^Cna*wW^-j>`(HnqJy(ptkWP?A zfgnG|W9U2kR%knM;Suk7{y-q{{fU&PmS*kK^rsEGO~@H0GFrN&BnaxFL~ )?OH97{_^37 z1VF;;y9@p1*!jL#=RG;Mhkiz%Y0^jq#N&_R9Ma?Z1SJFDCB8)xkFy!@p)(j7bD zMb(ruIeL0p+UjsFxm&yW$V8Zh$Y=kw*pGfL!WU#WXI77%bT5CU1=GewB@JU=Lv!g! zlBChoTS!D91Zhxw2;7*?{sI03G-?JAp)hTj-XAxDPlJLzOT@T*e)@V_PN$8yELLA% z<(zU5EY*Epe?Il x{Mr~Te~-;V(xSZS9j$al9J~TXJp3C(Jc3VSp3n=-_rDkMgI`*7? z;blSnY1Xac*sQxzlkYc1%lzK>$;Qg-bHAwN$;Zg+hD>+>VLG71omEq>PxW91pY<^O z5sHO-WjQW{hQU9`3ZR3~@cDnuT>>dcxhMRvyw9I&8=bMJ?en3@DEgA*kutJTyt(Yo z<{QMxI$|eZqlM4(CSL{)Hve*dVIjwDU{a>(I7uP&Zfa2s3zTxyOTqP52~F(GQkJ}| ztjguH39}vx1SR+ds5ph#yj=J|q{tz=6$AoOYlEEG0U8<7+ZY K2x_+Vnwm9r!rD{qF@uOP`8#_JJLl(wSU@?7>rPHr_0!FT=Pll_ptLyCvYu zmQgyAwMZiu5#&$X`=QsZ$7RF!weNZBN|E=JBnZ=c#B1lxmtehaUd4VQU%+KY_IQAk z4ig|NJ*q58gfU9lR>Wb&Z9xZYw+-7*0B4c)x*B~lTuK39(e_KBiBb;UOUVPa69adQ z@PbfuQEFwW%Y+U$c8XsRS>cRKI(LEU|CO4eX~Lg8T9zJ~2yagc6qeFw4_T-PWsb~K z!Q+^=j3exl61jJtFHZ@xBn&t?TMG_-4U}sV(-1T}Hoxk?-0KY$2srYs9pDzfIs79b zeL_%55kws44_K~Dh2(LyJHBd4znCPL*StX~agULJHhU-@aR>|GvucmMeVKE5oW+@; z{Ls7tK=O^JCE9hBEqO%#VhF%5>ziGbh7ZpZ6~~z9+5 Ih0unu+)8*;jo!=XnLA2k;@r3Z49w?dyfuj-p<;(IyXDZ+| zsW%YSmqvhZXQB@H5#O=*&;DuFvd0Oy0$oZ@ij_Lz&l&TwL|fopt8Foi<|yv5F69 zh-1t=SynrcTv+^+q@;B~_5Z!z+#q3u*>qz35t)y&K}b^XZ<;uQ!QrOL4oX!zv|JN_ z(1zx(8eRy=_Ln)d+s>*BooI7|0pHPJS(d{|Pq=F%A)RpNoNu<9N>#=TqUs5Vx$ApZ z_3-eB?Bf#pj|lsK;-(HH9` J!4GW~pQ`(v>kh5^m54!rXlO~J zkudpr3SoSb;MN0d$Q@jPt1i|>NSjKpi6Q<7)z4^fT{-*__sx*inq0+~FHFzJ)2`cz z07IJa#wcCM&4aeoguvy 7YUD9dJ#6m6w)+BzVf7Z0_y{sXw_06E+kyi z96GW5W(dA{kU>}TXJ!=OjpXwr($VMc8()6y`HkLCL(;~%`CA$P((JtVM9Ug-rxO2p zwQXHz?WWDWcHzR5tkp1(2HSO+?{pC3@oL@o@KCGi8-3cpb##({=%wGU#Nd@5sIT6~ z)XTaKHA3ItV_}7apM0%AeV8v0n1ajyAv1ouQ))oSA_vH7jeoalhu{lC9b;yGfsMm@ z^v|%qGgZl3fN->N`P~LVS9&9+HV8cN4*lctL%x}0kGQ$f$vQ{7jx 8blZ&_?~EkcJDBGR7QN@)CtG ^P1GA2+yTo+$GEpDPV*CDI|b@Q2C+kHqS=mSzrE zx;%Rad-Qm X%`4RLV#6JuhGp0N8V$B$y`(17=tvMc zEnXs@z<#QG3-K%r_Ep2wUuyT&f`|8|(C!$RJe{8!`9qOtP-z2ipo*M_!BBT^X};JO z&N&=oizt1oKgaGz$F_}XC6-HdjZAzkUXC20;b}c2ZhZEr;i3B9+17{;`b81)Y6jx+ zSa3GOo#LE~X{{E9hlYqK)=;nGzla&b?V=etnzo95DReLEkJH&}>Si}2kFNhRWAE8< z$ ;fl~W40_%yGV=Ygn*VIka+NenysWEmvwrh z-Z=E{q{Ih*@|e%npfdz~=W&M$br?+oJ~tCr37-{m5$nvZuqIOyTt(+5$VHRtesGZc z7t}-r(;WMSNAvSHRt|Sa-ZV7$6gt}*`tXe)nz%id2v01T3WDYgtvLrAgSz>_tiM^y z vf z0laUEeB000pO3w9pN`X59c8mp ^R3se6JwB3nMX$`{9rUxZtkFOBUrQQvV*_U z8zVK =i}tu7hGYziE4R52O%OR z5TZw#O#Dk$cnNwXpn3q+kvQnNZQAcQhN9qUng8np5R==^xo_7SKd`(4(E(gth2iMG z{;lvnx7T~Kd6}uMdgb@=K%8ic{kq4{{!~_OZlflJlnFyv+~MkUz{9w~$1gsxi3jDN zKYxhg?+Pj!wNa5 syu9Lql;lOQ zaK`p!i}C(g+g m6mf@Yv;K)D7G`;sA1ym{oCWU )~hZ{fC^ (Ue7B} zE8~XGF<=?hUsx2vw!Pc&_!L@;KvchCSRw{YQ7Tj+Oy8OF89&_#lp-8YCxUUg(eCDf zS%IRobHtouWND=IyuI6`0hcJ@ufqm Y?@5-s=ln#BWC$$VUB !zA;k*DgM$?d?t(k9D%F zu7=4P&g<)aD5@rm(R;d*04R4H>%$L-MzA17{ZsG-8zKo z_=3lM;?F0a8sg!v23GIASQdCq6(m0BY0fJ3xzIDS{y`1%0iZ;I_lDkw2}@Dk3*IYZ zh?*VL-5iq7r>f7}MKBD;R?Sjc8?Ajfy<<%8&eCB#0OR#IDdp&$wT+KLsCaQ|nFnkg zTzBZa6rtX$&mSEfRmkN8R{DVJK<{#vyI}H<0>;PzoN4=TDTO5OH<5RDcc^T*FYO_K zDjzL;#EZ<}igrBtRaF{Ti11})MOcRP9CsBn&h#_lFe}b)k~^sMFtTzMtU6C4AwDw> zo}^>2&AN^4)#AV9s}|YMz6(nyxbchxnWvPDp+-Pp$0bC{c%5IiG81R5`AlRCop4*A z(;y{r;^0UKV2?_JpaMXE!3cs>XF89WN2TYqVcAGV?Mn}VN+7;fc>bsxghS@0qObQ4 zK|vm#ugxu-tYAZKsLv*{-_ on0NZyFz-QqZc86sNq^+5_F9ep_pYY=iQdI%qyU zXnCyw+U%2DT+K)wqJ_cFl*Wu2Fj0}>shZ?p@oNb|!lU5$RjBO(7U%PldPij$mk>up z;8T8#*0C7EPDsk)M*N4#bj 7|+j8S;yaQW~;%)s#b+8OXYy$Y4#F z_+NC36J)NmSM0JnQ5Izu+$Cxz0@Renw*(SO2yT}ypfReHq@*IxAtv`$=XK9|n=G%y zE!25(qR#@@Hs9rOsmr$E1@{u|q`;|_b7R1@ Z z&JYNvz0Ay+)f|mTvz@}L;*XW@Fk?cZg@CSvCc%1ZM&wIOU=3X8U*$aVRwSfK+T_HK ze-4Lqf<-%mkl56lCul80ok-;f_Ci6UkKqGprORLUSRVi0`wdT>SMRWi6j?JEbXMZg z3EeJeSHpzglSl