From c675b73f251982f80c6fa25d39c3f03eaec71e05 Mon Sep 17 00:00:00 2001 From: yutent Date: Wed, 14 Jun 2023 19:36:20 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E5=A4=9A=E7=BA=BF=E7=A8=8B=E7=BC=96?= =?UTF-8?q?=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/compile-vue.js | 3 +- lib/compile.js | 92 ++++++++++++++++++++ lib/prod.js | 212 +++++++++++++++++++++------------------------ lib/thread.js | 24 +++++ 4 files changed, 217 insertions(+), 114 deletions(-) create mode 100644 lib/compile.js create mode 100644 lib/thread.js diff --git a/lib/compile-vue.js b/lib/compile-vue.js index f7deb8e..844ca43 100644 --- a/lib/compile-vue.js +++ b/lib/compile-vue.js @@ -317,8 +317,9 @@ export function compileVue(file, imports, options = {}) { isCustomElement }).code.replace('export function render', 'function render') } catch (err) { + // console.log(err) let tmp = html[1].split('\n') - let line = tmp[err.loc.start.line - 1] + let line = tmp[err.loc?.start.line - 1] console.log('%s: %s', red('SyntaxError'), red(err.message)) console.log( diff --git a/lib/compile.js b/lib/compile.js new file mode 100644 index 0000000..6f45b21 --- /dev/null +++ b/lib/compile.js @@ -0,0 +1,92 @@ +/** + * {} + * @author yutent + * @date 2023/06/14 18:36:15 + */ + +import { join, dirname, parse, normalize } from 'node:path' +import fs from 'iofs' +import Es from 'esbuild' +import { compileScss, parseJs, compileVue, parseHtml } from './compile-vue.js' + +const template = fs.cat(join(process.cwd(), 'index.html')).toString() + +export function compileFiles( + currentPage, + page, + files, + options, + { verbose, imports, dist } = {} +) { + options.currentPage = currentPage + + console.log('正在生成 %s ...', `${currentPage}.html`) + + for (let [k, it] of files) { + // let it = files[k] + // 入口文件, 特殊处理 + if (page && it.path === page.entry) { + let entry = fs.cat(page.entry).toString() + + entry = parseJs(entry, imports, { ...options, IS_ENTRY: true }) + + let code = parseHtml(template, { + page, + imports, + entry, + LEGACY_MODE: options.LEGACY_MODE + }) + + fs.echo(code, join(dist, `${currentPage}.html`)) + continue + } + + verbose && console.log(' 解析 %s ...', it.name) + + let pageDir = options.IS_MPA && currentPage ? `pages/${currentPage}` : '' + + switch (it.ext) { + case '.vue': + { + let code = compileVue(it.path, imports, options) + + Es.transform(code, { minify: true }).then(r => { + fs.echo( + r.code, + join(dist, 'assets/', pageDir, it.name.replace(/\.vue$/, '.js')) + ) + }) + } + break + + case '.js': + { + let code = fs.cat(it.path) + + code = parseJs(code + '', imports, options) + + Es.transform(code, { minify: true }).then(r => { + fs.echo(r.code, join(dist, 'assets/', pageDir, it.name)) + }) + } + break + + case '.scss': + case '.css': + { + let target = join(dist, 'assets/', it.name.replace(/\.scss$/, '.css')) + if (it.ext === '.css') { + fs.cp(it.path, target) + } else { + let code = compileScss(it.path) + fs.echo(code, target) + } + } + break + + default: + fs.cp(it.path, join(dist, it.name)) + break + } + } +} diff --git a/lib/prod.js b/lib/prod.js index fda4a0c..fc5cfc2 100644 --- a/lib/prod.js +++ b/lib/prod.js @@ -1,11 +1,16 @@ -import { join, dirname, parse } from 'node:path' +import { join, dirname, parse, normalize } from 'node:path' +import { Worker } from 'node:worker_threads' +import os from 'node:os' import fs from 'iofs' -import Es from 'esbuild' -import { compileScss, parseJs, compileVue, parseHtml } from './compile-vue.js' +// import Es from 'esbuild' +// import { compileScss, parseJs, compileVue, parseHtml } from './compile-vue.js' import { isCustomElement } from './utils.js' +import { compileFiles } from './compile.js' const IS_WIN = process.platform === 'win32' const PREFIX = IS_WIN ? 'pages\\' : 'pages/' +const THREADS_NUM = os.cpus().length +const __dirname = normalize(dirname(import.meta.url.slice(IS_WIN ? 8 : 7))) function readFile(file) { return (file && fs.cat(file)?.toString()) || '' @@ -16,8 +21,9 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { const SOURCE_DIR = join(root, 'src') const PUBLIC_DIR = join(root, 'public') const DEPLOY_PATH = conf.base || '' // 部署目录, 默认是根目录部署 - const IS_MPA = Object.keys(conf.pages).length > 1 - const PAGES_PREFIX = Object.keys(conf.pages).map(it => + const PAGES_KEYS = Object.keys(conf.pages) + const IS_MPA = PAGES_KEYS.length > 1 + const PAGES_PREFIX = PAGES_KEYS.map(it => IS_WIN ? `${PREFIX + it}\\` : `${PREFIX + it}/` ) const INJECT_SCSS = readFile(conf.inject?.scss) @@ -27,7 +33,17 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { let timeStart = Date.now() let template = fs.cat(join(process.cwd(), 'index.html')).toString() - let list = {} + let list = new Map() + let options = { + IS_MPA, + SOURCE_DIR, + DEPLOY_PATH, + INJECT_SCSS, + LEGACY_MODE, + // 线程通讯无法传递函数类型, 需要转为字符串, 之后再转回来 + // isCustomElement: conf.isCustomElement || isCustomElement + isCustomElement: (conf.isCustomElement || isCustomElement).toString() + } fs.ls(SOURCE_DIR, true).forEach(path => { if (fs.isdir(path)) { @@ -43,7 +59,7 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { if (it.ext !== '') { if (IS_MPA && it.name.startsWith(PREFIX)) { if (PAGES_PREFIX.some(it => it.startsWith(it.name))) { - return (list[path] = it) + return list.set(path, it) } else { return } @@ -53,94 +69,10 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { return } - list[path] = it + list.set(path, it) } }) - let compileFiles = function (currentPage, page, files) { - let options = { - IS_MPA, - currentPage, - SOURCE_DIR, - DEPLOY_PATH, - INJECT_SCSS, - LEGACY_MODE, - isCustomElement: conf.isCustomElement || isCustomElement - } - - for (let k in files) { - let it = files[k] - // 入口文件, 特殊处理 - if (page && it.path === page.entry) { - let entry = fs.cat(page.entry).toString() - - entry = parseJs(entry, conf.imports, { ...options, IS_ENTRY: true }) - - let code = parseHtml(template, { - page, - imports: conf.imports, - entry, - LEGACY_MODE - }) - - fs.echo(code, join(dist, `${currentPage}.html`)) - continue - } - - verbose && console.log(' 解析 %s ...', it.name) - - let pageDir = IS_MPA && currentPage ? `pages/${currentPage}` : '' - - switch (it.ext) { - case '.vue': - { - let code = compileVue(it.path, conf.imports, options) - - Es.transform(code, { minify: true }).then(r => { - fs.echo( - r.code, - join(dist, 'assets/', pageDir, it.name.replace(/\.vue$/, '.js')) - ) - }) - } - break - - case '.js': - { - let code = fs.cat(it.path) - - code = parseJs(code + '', conf.imports, options) - - Es.transform(code, { minify: true }).then(r => { - fs.echo(r.code, join(dist, 'assets/', pageDir, it.name)) - }) - } - break - - case '.scss': - case '.css': - { - let target = join( - dist, - 'assets/', - it.name.replace(/\.scss$/, '.css') - ) - if (it.ext === '.css') { - fs.cp(it.path, target) - } else { - let code = compileScss(it.path) - fs.echo(code, target) - } - } - break - - default: - fs.cp(it.path, join(dist, it.name)) - break - } - } - } - // 优先处理静态目录, 之后的源码目录中, 以便如果有产生相同的文件名, 则覆盖静态目录中的文件 if (fs.isdir(PUBLIC_DIR)) { console.log('\n正在处理静态资源 ...') @@ -160,35 +92,89 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { } console.log('') - for (let currentPage in conf.pages) { + if (IS_MPA) { + // 电脑线程数比页面数量还多时, 取小 + let num = Math.min(PAGES_KEYS.length, THREADS_NUM) + let chunkSize = Math.ceil(PAGES_KEYS.length / num) + + for (let i = 0; i < num; i++) { + let start = i * chunkSize + let end = start + chunkSize + let pages = PAGES_KEYS.slice(start, end) + let chunk = new Map() + + for (let currentPage of pages) { + let page = conf.pages[currentPage] + let dir = dirname(page.entry) + let files = new Map() + + fs.ls(dir, true).forEach(path => { + if (fs.isdir(path)) { + return + } + + let name = path.slice(dir.length + 1) + let ext = parse(name).ext + + if (ext === '') { + return + } + + list.delete(path) + files.set(path, { name, path, ext }) + }) + + chunk.set(currentPage, { page, files }) + } + new Worker(join(__dirname, './thread.js'), { + workerData: { + options, + data: { + chunk, + verbose, + dist, + imports: conf.imports + } + } + }) + // compileFiles(currentPage, page, chunk, options, { + // verbose, + // dist, + // imports: conf.imports + // }) + } + } else { + // 每个线程处理的文件数 + let chunkSize = Math.ceil(list.size / THREADS_NUM) + let currentPage = PAGES_KEYS[0] let page = conf.pages[currentPage] - let dir = dirname(page.entry) - let files = list - if (IS_MPA) { - files = {} - fs.ls(dir, true).forEach(path => { - if (fs.isdir(path)) { - return + + list = [...list] + + for (let i = 0; i < THREADS_NUM; i++) { + let start = i * chunkSize + let end = start + chunkSize + let chunk = [list.slice(start, end)] + + new Worker(join(__dirname, './thread.js'), { + workerData: { + options, + data: { + currentPage, + page, + chunk, + verbose, + dist, + imports: conf.imports + } } - - let name = path.slice(dir.length + 1) - let ext = parse(name).ext - - if (ext === '') { - return - } - - delete list[path] - files[path] = { name, path, ext } }) } - console.log('正在生成 %s ...', `${currentPage}.html`) - compileFiles(currentPage, page, files) } if (IS_MPA) { console.log('\n正在解析公共依赖 ...') - compileFiles('', null, list) + // compileFiles('', null, list) } process.on('exit', _ => { diff --git a/lib/thread.js b/lib/thread.js new file mode 100644 index 0000000..6e84968 --- /dev/null +++ b/lib/thread.js @@ -0,0 +1,24 @@ +/** + * 子线程 + * @author yutent + * @date 2023/06/14 16:15:39 + */ +import { workerData } from 'node:worker_threads' +import { compileFiles } from './compile.js' + +const { options, data } = workerData + +options.isCustomElement = Function('return ' + options.isCustomElement)() + +// console.log(options) + +let chunk = data.chunk + +delete data.chunk + +chunk.forEach(([a, b]) => { + console.log(a, b) +}) +// for (let [currentPage, { page, files }] of chunk.entries()) { +// compileFiles(currentPage, page, files, options, { ...data }) +// } From ff5a225cc1113d0c020ac6e1abee084786f8b7ef Mon Sep 17 00:00:00 2001 From: yutent Date: Thu, 15 Jun 2023 16:54:45 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E6=9A=82=E5=AD=986.15?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/compile.js | 3 --- lib/prod.js | 50 ++++++++++++++++++++++++++++++++------------------ lib/thread.js | 14 ++++---------- 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/lib/compile.js b/lib/compile.js index 6f45b21..cc9dffd 100644 --- a/lib/compile.js +++ b/lib/compile.js @@ -20,10 +20,7 @@ export function compileFiles( ) { options.currentPage = currentPage - console.log('正在生成 %s ...', `${currentPage}.html`) - for (let [k, it] of files) { - // let it = files[k] // 入口文件, 特殊处理 if (page && it.path === page.entry) { let entry = fs.cat(page.entry).toString() diff --git a/lib/prod.js b/lib/prod.js index fc5cfc2..637280d 100644 --- a/lib/prod.js +++ b/lib/prod.js @@ -2,15 +2,16 @@ import { join, dirname, parse, normalize } from 'node:path' import { Worker } from 'node:worker_threads' import os from 'node:os' import fs from 'iofs' -// import Es from 'esbuild' -// import { compileScss, parseJs, compileVue, parseHtml } from './compile-vue.js' + import { isCustomElement } from './utils.js' -import { compileFiles } from './compile.js' const IS_WIN = process.platform === 'win32' const PREFIX = IS_WIN ? 'pages\\' : 'pages/' -const THREADS_NUM = os.cpus().length -const __dirname = normalize(dirname(import.meta.url.slice(IS_WIN ? 8 : 7))) +// 线程太多, 效率反而不高 +const THREADS_NUM = os.cpus().length > 4 ? 4 : os.cpus().length - 1 +// const THREADS_NUM = os.cpus().length +const __filename = normalize(import.meta.url.slice(IS_WIN ? 8 : 7)) +const __dirname = dirname(__filename) function readFile(file) { return (file && fs.cat(file)?.toString()) || '' @@ -59,10 +60,9 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { if (it.ext !== '') { if (IS_MPA && it.name.startsWith(PREFIX)) { if (PAGES_PREFIX.some(it => it.startsWith(it.name))) { - return list.set(path, it) - } else { - return + list.set(path, it) } + return } if (it.path === conf.inject.scss) { @@ -90,7 +90,6 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { } }) } - console.log('') if (IS_MPA) { // 电脑线程数比页面数量还多时, 取小 @@ -126,6 +125,7 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { chunk.set(currentPage, { page, files }) } + new Worker(join(__dirname, './thread.js'), { workerData: { options, @@ -133,15 +133,12 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { chunk, verbose, dist, - imports: conf.imports + imports: conf.imports, + timeStart: Date.now(), + title: '正在解析currentPage' } } }) - // compileFiles(currentPage, page, chunk, options, { - // verbose, - // dist, - // imports: conf.imports - // }) } } else { // 每个线程处理的文件数 @@ -154,14 +151,14 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { for (let i = 0; i < THREADS_NUM; i++) { let start = i * chunkSize let end = start + chunkSize - let chunk = [list.slice(start, end)] + let chunk = new Map() + + chunk.set(currentPage, { page, files: list.slice(start, end) }) new Worker(join(__dirname, './thread.js'), { workerData: { options, data: { - currentPage, - page, chunk, verbose, dist, @@ -175,6 +172,23 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { if (IS_MPA) { console.log('\n正在解析公共依赖 ...') // compileFiles('', null, list) + let chunk = new Map() + + chunk.set('', { page: null, files: [...list] }) + + new Worker(join(__dirname, './thread.js'), { + workerData: { + options, + data: { + chunk, + verbose, + dist, + imports: conf.imports, + timeStart: Date.now(), + title: '正在解析公共依赖' + } + } + }) } process.on('exit', _ => { diff --git a/lib/thread.js b/lib/thread.js index 6e84968..0058072 100644 --- a/lib/thread.js +++ b/lib/thread.js @@ -10,15 +10,9 @@ const { options, data } = workerData options.isCustomElement = Function('return ' + options.isCustomElement)() -// console.log(options) +console.log('data: ', data.title, Date.now() - data.timeStart) -let chunk = data.chunk - -delete data.chunk - -chunk.forEach(([a, b]) => { - console.log(a, b) -}) -// for (let [currentPage, { page, files }] of chunk.entries()) { -// compileFiles(currentPage, page, files, options, { ...data }) +// for (let [currentPage, { page, files }] of data.chunk.entries()) { +// currentPage && console.log('正在生成 %s ...', `${currentPage}.html`) +// compileFiles(currentPage, page, files, options, data) // } From 50a54a152cc61a08d7742913488bf356c17ec306 Mon Sep 17 00:00:00 2001 From: yutent Date: Thu, 15 Jun 2023 19:43:21 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/prod.js | 142 +++++++++++++++++++++++++++----------------------- lib/thread.js | 17 +++--- 2 files changed, 86 insertions(+), 73 deletions(-) diff --git a/lib/prod.js b/lib/prod.js index 637280d..2dd88a4 100644 --- a/lib/prod.js +++ b/lib/prod.js @@ -9,14 +9,36 @@ const IS_WIN = process.platform === 'win32' const PREFIX = IS_WIN ? 'pages\\' : 'pages/' // 线程太多, 效率反而不高 const THREADS_NUM = os.cpus().length > 4 ? 4 : os.cpus().length - 1 -// const THREADS_NUM = os.cpus().length const __filename = normalize(import.meta.url.slice(IS_WIN ? 8 : 7)) const __dirname = dirname(__filename) +const WORKER_POOL = new Set() // 线程池 +const JOBS_QUEUE = [] // 任务队列 function readFile(file) { return (file && fs.cat(file)?.toString()) || '' } +function doJob() { + // console.log('<><><>', JOBS_QUEUE.length, WORKER_POOL.size) + // if (JOBS_QUEUE.length === 0 && WORKER_POOL.size === THREADS_NUM) { + // process.exit() + // } + + while (JOBS_QUEUE.length && WORKER_POOL.size) { + let job = JOBS_QUEUE.shift() + let worker = WORKER_POOL.values().next().value + + WORKER_POOL.delete(worker) + + worker.once('message', _ => { + WORKER_POOL.add(worker) + + doJob() + }) + worker.postMessage(job) + } +} + export default function compile(root = '', dist = '', conf = {}, verbose) { // const SOURCE_DIR = join(root, 'src') @@ -91,54 +113,64 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { }) } - if (IS_MPA) { - // 电脑线程数比页面数量还多时, 取小 - let num = Math.min(PAGES_KEYS.length, THREADS_NUM) - let chunkSize = Math.ceil(PAGES_KEYS.length / num) - - for (let i = 0; i < num; i++) { - let start = i * chunkSize - let end = start + chunkSize - let pages = PAGES_KEYS.slice(start, end) - let chunk = new Map() - - for (let currentPage of pages) { - let page = conf.pages[currentPage] - let dir = dirname(page.entry) - let files = new Map() - - fs.ls(dir, true).forEach(path => { - if (fs.isdir(path)) { - return - } - - let name = path.slice(dir.length + 1) - let ext = parse(name).ext - - if (ext === '') { - return - } - - list.delete(path) - files.set(path, { name, path, ext }) - }) - - chunk.set(currentPage, { page, files }) - } - + // 创建线程池 + for (let i = 0; i < THREADS_NUM; i++) { + WORKER_POOL.add( new Worker(join(__dirname, './thread.js'), { workerData: { options, - data: { - chunk, - verbose, - dist, - imports: conf.imports, - timeStart: Date.now(), - title: '正在解析currentPage' - } + verbose, + dist, + imports: conf.imports } }) + ) + } + + if (IS_MPA) { + // 电脑线程数比页面数量还多时, 取小 + // let num = Math.min(PAGES_KEYS.length, THREADS_NUM) + // let chunkSize = Math.ceil(PAGES_KEYS.length / num) + + // for (let i = 0; i < num; i++) { + // let start = i * chunkSize + // let end = start + chunkSize + // let pages = PAGES_KEYS.slice(start, end) + // let chunk = new Map() + + for (let currentPage of PAGES_KEYS) { + let page = conf.pages[currentPage] + let dir = dirname(page.entry) + let files = new Map() + let chunk = new Map() + + fs.ls(dir, true).forEach(path => { + if (fs.isdir(path)) { + return + } + + let name = path.slice(dir.length + 1) + let ext = parse(name).ext + + if (ext === '') { + return + } + + list.delete(path) + files.set(path, { name, path, ext }) + }) + chunk.set(currentPage, { page, files }) + JOBS_QUEUE.push(chunk) + doJob() + } + + // + { + let chunk = new Map() + + chunk.set('', { page: null, files: list }) + JOBS_QUEUE.push(chunk) + doJob() } } else { // 每个线程处理的文件数 @@ -169,28 +201,6 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { } } - if (IS_MPA) { - console.log('\n正在解析公共依赖 ...') - // compileFiles('', null, list) - let chunk = new Map() - - chunk.set('', { page: null, files: [...list] }) - - new Worker(join(__dirname, './thread.js'), { - workerData: { - options, - data: { - chunk, - verbose, - dist, - imports: conf.imports, - timeStart: Date.now(), - title: '正在解析公共依赖' - } - } - }) - } - process.on('exit', _ => { console.log('\n页面处理完成, 耗时 %ss\n', (Date.now() - timeStart) / 1000) }) diff --git a/lib/thread.js b/lib/thread.js index 0058072..c23b664 100644 --- a/lib/thread.js +++ b/lib/thread.js @@ -3,16 +3,19 @@ * @author yutent * @date 2023/06/14 16:15:39 */ -import { workerData } from 'node:worker_threads' +import { parentPort, workerData } from 'node:worker_threads' import { compileFiles } from './compile.js' -const { options, data } = workerData +const { options, verbose, dist, imports } = workerData options.isCustomElement = Function('return ' + options.isCustomElement)() -console.log('data: ', data.title, Date.now() - data.timeStart) +parentPort.once('message', job => { + let [currentPage, { page, files }] = job.entries().next().value -// for (let [currentPage, { page, files }] of data.chunk.entries()) { -// currentPage && console.log('正在生成 %s ...', `${currentPage}.html`) -// compileFiles(currentPage, page, files, options, data) -// } + console.log( + currentPage ? `正在生成 ${currentPage}.html ...` : '\n正在解析公共依赖 ...' + ) + compileFiles(currentPage, page, files, options, { verbose, dist, imports }) + parentPort.postMessage('ok') +}) From 82c8686bfb46b49c424f117e1930d2c9b8d08561 Mon Sep 17 00:00:00 2001 From: yutent Date: Thu, 15 Jun 2023 19:51:21 +0800 Subject: [PATCH 4/6] once -> on --- lib/thread.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/thread.js b/lib/thread.js index c23b664..318a3a3 100644 --- a/lib/thread.js +++ b/lib/thread.js @@ -10,7 +10,7 @@ const { options, verbose, dist, imports } = workerData options.isCustomElement = Function('return ' + options.isCustomElement)() -parentPort.once('message', job => { +parentPort.on('message', job => { let [currentPage, { page, files }] = job.entries().next().value console.log( From cca39d915619693ca5577bdbc896db5ee0d23d30 Mon Sep 17 00:00:00 2001 From: yutent Date: Fri, 16 Jun 2023 12:11:43 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=A4=9A=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E7=BC=96=E8=AF=91,=20=E4=BF=AE=E5=A4=8D=E9=9D=99?= =?UTF-8?q?=E6=80=81=E6=96=87=E4=BB=B6=E5=A4=8D=E5=88=B6=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/compile.js | 15 ++++++++++----- lib/prod.js | 32 +++++++++----------------------- lib/thread.js | 13 ++++++++++--- lib/utils.js | 14 -------------- lib/ws.js | 2 +- 5 files changed, 30 insertions(+), 46 deletions(-) diff --git a/lib/compile.js b/lib/compile.js index cc9dffd..5c5b560 100644 --- a/lib/compile.js +++ b/lib/compile.js @@ -11,7 +11,7 @@ import { compileScss, parseJs, compileVue, parseHtml } from './compile-vue.js' const template = fs.cat(join(process.cwd(), 'index.html')).toString() -export function compileFiles( +export async function compileFiles( currentPage, page, files, @@ -47,7 +47,7 @@ export function compileFiles( { let code = compileVue(it.path, imports, options) - Es.transform(code, { minify: true }).then(r => { + await Es.transform(code, { minify: true }).then(r => { fs.echo( r.code, join(dist, 'assets/', pageDir, it.name.replace(/\.vue$/, '.js')) @@ -62,7 +62,7 @@ export function compileFiles( code = parseJs(code + '', imports, options) - Es.transform(code, { minify: true }).then(r => { + await Es.transform(code, { minify: true }).then(r => { fs.echo(r.code, join(dist, 'assets/', pageDir, it.name)) }) } @@ -71,7 +71,12 @@ export function compileFiles( case '.scss': case '.css': { - let target = join(dist, 'assets/', it.name.replace(/\.scss$/, '.css')) + let target = join( + dist, + 'assets/', + pageDir, + it.name.replace(/\.scss$/, '.css') + ) if (it.ext === '.css') { fs.cp(it.path, target) } else { @@ -82,7 +87,7 @@ export function compileFiles( break default: - fs.cp(it.path, join(dist, it.name)) + fs.cp(it.path, join(dist, 'assets/', pageDir, it.name)) break } } diff --git a/lib/prod.js b/lib/prod.js index 2dd88a4..48bbae0 100644 --- a/lib/prod.js +++ b/lib/prod.js @@ -1,10 +1,8 @@ import { join, dirname, parse, normalize } from 'node:path' -import { Worker } from 'node:worker_threads' +import { Worker, parentPort } from 'node:worker_threads' import os from 'node:os' import fs from 'iofs' -import { isCustomElement } from './utils.js' - const IS_WIN = process.platform === 'win32' const PREFIX = IS_WIN ? 'pages\\' : 'pages/' // 线程太多, 效率反而不高 @@ -19,11 +17,6 @@ function readFile(file) { } function doJob() { - // console.log('<><><>', JOBS_QUEUE.length, WORKER_POOL.size) - // if (JOBS_QUEUE.length === 0 && WORKER_POOL.size === THREADS_NUM) { - // process.exit() - // } - while (JOBS_QUEUE.length && WORKER_POOL.size) { let job = JOBS_QUEUE.shift() let worker = WORKER_POOL.values().next().value @@ -31,10 +24,14 @@ function doJob() { WORKER_POOL.delete(worker) worker.once('message', _ => { - WORKER_POOL.add(worker) - - doJob() + if (JOBS_QUEUE.length) { + WORKER_POOL.add(worker) + doJob() + } else { + worker.terminate() + } }) + worker.postMessage(job) } } @@ -64,8 +61,7 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { INJECT_SCSS, LEGACY_MODE, // 线程通讯无法传递函数类型, 需要转为字符串, 之后再转回来 - // isCustomElement: conf.isCustomElement || isCustomElement - isCustomElement: (conf.isCustomElement || isCustomElement).toString() + isCustomElement: conf.isCustomElement } fs.ls(SOURCE_DIR, true).forEach(path => { @@ -128,16 +124,6 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { } if (IS_MPA) { - // 电脑线程数比页面数量还多时, 取小 - // let num = Math.min(PAGES_KEYS.length, THREADS_NUM) - // let chunkSize = Math.ceil(PAGES_KEYS.length / num) - - // for (let i = 0; i < num; i++) { - // let start = i * chunkSize - // let end = start + chunkSize - // let pages = PAGES_KEYS.slice(start, end) - // let chunk = new Map() - for (let currentPage of PAGES_KEYS) { let page = conf.pages[currentPage] let dir = dirname(page.entry) diff --git a/lib/thread.js b/lib/thread.js index 318a3a3..43f35e4 100644 --- a/lib/thread.js +++ b/lib/thread.js @@ -5,17 +5,24 @@ */ import { parentPort, workerData } from 'node:worker_threads' import { compileFiles } from './compile.js' +import { isCustomElement } from './utils.js' const { options, verbose, dist, imports } = workerData options.isCustomElement = Function('return ' + options.isCustomElement)() -parentPort.on('message', job => { +async function doJob(job) { let [currentPage, { page, files }] = job.entries().next().value console.log( currentPage ? `正在生成 ${currentPage}.html ...` : '\n正在解析公共依赖 ...' ) - compileFiles(currentPage, page, files, options, { verbose, dist, imports }) + await compileFiles(currentPage, page, files, options, { + verbose, + dist, + imports + }) parentPort.postMessage('ok') -}) +} + +parentPort.on('message', doJob) diff --git a/lib/utils.js b/lib/utils.js index 68f2f1c..9919451 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -7,7 +7,6 @@ import { createHash, randomUUID } from 'node:crypto' import { join } from 'node:path' import { gzipSync } from 'node:zlib' -import { Worker } from 'node:worker_threads' import { red, cyan, blue } from 'kolorist' // 修正路径合并 避免在windows下被转义 @@ -98,16 +97,3 @@ export function createHmrScript(legacy, session = '') { export function isCustomElement(tag) { return tag.startsWith('wc-') } - -export function startWorker(file) { - return new Promise((resolve, reject) => { - let worker = new Worker(file) - worker.on('message', resolve) - worker.on('error', reject) - worker.on('exit', code => { - if (code !== 0) { - reject(new Error(`Worker stopped with exit code ${code}`)) - } - }) - }) -} diff --git a/lib/ws.js b/lib/ws.js index a495f48..208f45f 100644 --- a/lib/ws.js +++ b/lib/ws.js @@ -32,7 +32,7 @@ class WebSocket { send(msg = {}) { if (this.#clients.size) { - for (let [key, client] of this.#clients.entries()) { + for (let [key, client] of this.#clients) { client.send(JSON.stringify(msg)) } } else { From ea986f0a392e6ba4edd511d0df6fbc5e1e2c565f Mon Sep 17 00:00:00 2001 From: yutent Date: Fri, 16 Jun 2023 14:19:32 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=A4=9A=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E7=BC=96=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/compile.js | 14 ++++++------ lib/dev.js | 7 +++--- lib/prod.js | 61 +++++++++++++++++++++----------------------------- lib/thread.js | 16 ++++++++----- lib/utils.js | 3 ++- 5 files changed, 49 insertions(+), 52 deletions(-) diff --git a/lib/compile.js b/lib/compile.js index 5c5b560..5b3509c 100644 --- a/lib/compile.js +++ b/lib/compile.js @@ -20,9 +20,9 @@ export async function compileFiles( ) { options.currentPage = currentPage - for (let [k, it] of files) { + for (let [path, it] of files) { // 入口文件, 特殊处理 - if (page && it.path === page.entry) { + if (page && path === page.entry) { let entry = fs.cat(page.entry).toString() entry = parseJs(entry, imports, { ...options, IS_ENTRY: true }) @@ -45,7 +45,7 @@ export async function compileFiles( switch (it.ext) { case '.vue': { - let code = compileVue(it.path, imports, options) + let code = compileVue(path, imports, options) await Es.transform(code, { minify: true }).then(r => { fs.echo( @@ -58,7 +58,7 @@ export async function compileFiles( case '.js': { - let code = fs.cat(it.path) + let code = fs.cat(path) code = parseJs(code + '', imports, options) @@ -78,16 +78,16 @@ export async function compileFiles( it.name.replace(/\.scss$/, '.css') ) if (it.ext === '.css') { - fs.cp(it.path, target) + fs.cp(path, target) } else { - let code = compileScss(it.path) + let code = compileScss(path) fs.echo(code, target) } } break default: - fs.cp(it.path, join(dist, 'assets/', pageDir, it.name)) + fs.cp(path, join(dist, 'assets/', pageDir, it.name)) break } } diff --git a/lib/dev.js b/lib/dev.js index 1756197..2ab47dd 100644 --- a/lib/dev.js +++ b/lib/dev.js @@ -7,7 +7,7 @@ import socket from './ws.js' import chokidar from 'chokidar' import { red } from 'kolorist' -import { friendlyErrors, isCustomElement, md5, gzip } from './utils.js' +import { friendlyErrors, defaultCustomElement, md5, gzip } from './utils.js' import { compileScss, parseJs, compileVue, parseHtml } from './compile-vue.js' @@ -33,6 +33,7 @@ export default async function createServer(root = '', conf = {}) { const INJECT_SCSS = readFile(conf.inject?.scss) const LEGACY_MODE = !!conf.legacy const ENABLE_GZIP = !!conf.devServer.gzip + const { isCustomElement = defaultCustomElement } = conf.compileOptions || {} if (conf.imports['vue-dev']) { conf.imports.vue = conf.imports['vue-dev'] @@ -215,7 +216,7 @@ export default async function createServer(root = '', conf = {}) { DEPLOY_PATH, INJECT_SCSS, LEGACY_MODE, - isCustomElement: conf.isCustomElement || isCustomElement + isCustomElement }) res.setHeader('content-type', MIME_TYPES.js) @@ -395,7 +396,7 @@ export default async function createServer(root = '', conf = {}) { DEPLOY_PATH, INJECT_SCSS, LEGACY_MODE, - isCustomElement: conf.isCustomElement || isCustomElement + isCustomElement }) let tmp = CACHE[filePath] if (tmp.changed) { diff --git a/lib/prod.js b/lib/prod.js index 48bbae0..1c1151b 100644 --- a/lib/prod.js +++ b/lib/prod.js @@ -48,6 +48,7 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { ) const INJECT_SCSS = readFile(conf.inject?.scss) const LEGACY_MODE = !!conf.legacy + const { isCustomElement } = conf.compileOptions || {} conf.inject = conf.inject || { scss: '' } @@ -61,7 +62,21 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { INJECT_SCSS, LEGACY_MODE, // 线程通讯无法传递函数类型, 需要转为字符串, 之后再转回来 - isCustomElement: conf.isCustomElement + isCustomElement: isCustomElement ? isCustomElement.toString() : null + } + + // 创建线程池 + for (let i = 0; i < THREADS_NUM; i++) { + WORKER_POOL.add( + new Worker(join(__dirname, './thread.js'), { + workerData: { + options, + verbose, + dist, + imports: conf.imports + } + }) + ) } fs.ls(SOURCE_DIR, true).forEach(path => { @@ -71,11 +86,10 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { let name = path.slice(SOURCE_DIR.length + 1) let it = { - path, name, ext: parse(name).ext } - if (it.ext !== '') { + if (it.ext) { if (IS_MPA && it.name.startsWith(PREFIX)) { if (PAGES_PREFIX.some(it => it.startsWith(it.name))) { list.set(path, it) @@ -97,11 +111,7 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { fs.ls(PUBLIC_DIR, true).forEach(it => { let ext = parse(it).ext - if (ext === '') { - return - } - - if (fs.isfile(it)) { + if (ext && fs.isfile(it)) { let name = it.slice(PUBLIC_DIR.length + 1) verbose && console.log(' 复制 %s ...', name) fs.cp(it, join(dist, name)) @@ -109,20 +119,6 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { }) } - // 创建线程池 - for (let i = 0; i < THREADS_NUM; i++) { - WORKER_POOL.add( - new Worker(join(__dirname, './thread.js'), { - workerData: { - options, - verbose, - dist, - imports: conf.imports - } - }) - ) - } - if (IS_MPA) { for (let currentPage of PAGES_KEYS) { let page = conf.pages[currentPage] @@ -143,18 +139,18 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { } list.delete(path) - files.set(path, { name, path, ext }) + files.set(path, { name, ext }) }) chunk.set(currentPage, { page, files }) JOBS_QUEUE.push(chunk) doJob() } - // + // 公共依赖 { let chunk = new Map() - chunk.set('', { page: null, files: list }) + JOBS_QUEUE.push(chunk) doJob() } @@ -166,6 +162,8 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { list = [...list] + console.log(`正在生成 ${currentPage}.html ...`) + for (let i = 0; i < THREADS_NUM; i++) { let start = i * chunkSize let end = start + chunkSize @@ -173,17 +171,8 @@ export default function compile(root = '', dist = '', conf = {}, verbose) { chunk.set(currentPage, { page, files: list.slice(start, end) }) - new Worker(join(__dirname, './thread.js'), { - workerData: { - options, - data: { - chunk, - verbose, - dist, - imports: conf.imports - } - } - }) + JOBS_QUEUE.push(chunk) + doJob() } } diff --git a/lib/thread.js b/lib/thread.js index 43f35e4..1f9fad3 100644 --- a/lib/thread.js +++ b/lib/thread.js @@ -5,18 +5,24 @@ */ import { parentPort, workerData } from 'node:worker_threads' import { compileFiles } from './compile.js' -import { isCustomElement } from './utils.js' +import { defaultCustomElement } from './utils.js' const { options, verbose, dist, imports } = workerData -options.isCustomElement = Function('return ' + options.isCustomElement)() +options.isCustomElement = options.isCustomElement + ? Function('return ' + options.isCustomElement)() + : defaultCustomElement async function doJob(job) { let [currentPage, { page, files }] = job.entries().next().value - console.log( - currentPage ? `正在生成 ${currentPage}.html ...` : '\n正在解析公共依赖 ...' - ) + options.IS_MPA && + console.log( + currentPage + ? `正在生成 ${currentPage}.html ...` + : '\n正在解析公共依赖 ...' + ) + await compileFiles(currentPage, page, files, options, { verbose, dist, diff --git a/lib/utils.js b/lib/utils.js index 9919451..b2c8808 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -94,6 +94,7 @@ export function createHmrScript(legacy, session = '') { ` } -export function isCustomElement(tag) { +// 默认的 web components 判断 +export function defaultCustomElement(tag) { return tag.startsWith('wc-') }