diff --git a/build/cli.js b/build/cli.js index ddcce96..c5fc2ef 100755 --- a/build/cli.js +++ b/build/cli.js @@ -1,8 +1,6 @@ #!/usr/bin/env node import { rollupOptions } from './rollup.js'; import { readJson } from './utils.js'; -import Koa from 'koa'; -import { extname } from 'node:path'; import { parseArgs } from 'node:util'; import { rollup } from 'rollup'; import { watch } from 'rollup'; @@ -42,39 +40,15 @@ if (!args.dev) { locale: args.locale, dev: true, }); - let html = ''; - const koa = new Koa(); - koa.use(async (ctx) => { - ctx.body = html; - }); - koa.listen(3000); - console.log('listen 3000'); const watcher = watch({ ...inputOptions, + output: outputOptions, watch: { buildDelay: 1000, clearScreen: false, exclude: ['node_modules/**', 'dist/**'], - skipWrite: true, }, }); - watcher.on('event', (args) => { - if (args.code === 'BUNDLE_END') { - console.log('BUNDLE_END'); - args.result.generate(outputOptions).then(({ output }) => { - html = - ''; - output.reverse().forEach((file) => { - if ( - extname(file.fileName) === '.html' && - !file.fileName.endsWith('back.html') - ) { - html += file.source; - } - }); - }); - } - }); watcher.on('event', ({ result }) => { if (result) { result.close(); diff --git a/build/plugins/dev-server/index.js b/build/plugins/dev-server/index.js new file mode 100644 index 0000000..5d19729 --- /dev/null +++ b/build/plugins/dev-server/index.js @@ -0,0 +1,61 @@ +import Router from '@koa/router'; +import Koa from 'koa'; +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { PassThrough } from 'node:stream'; + +/** @returns {import("rollup").Plugin} */ +const devServer = ({ port = 3000 } = {}) => { + const koa = new Koa(); + const router = new Router(); + + let sendCommand = null; + router.get('/__dev_server', (ctx) => { + ctx.set('Content-Type', 'text/event-stream'); + ctx.set('Cache-Control', 'no-cache'); + ctx.set('Connection', 'keep-alive'); + const stream = new PassThrough(); + ctx.status = 200; + ctx.body = stream; + sendCommand = (cmd) => { + stream.write(`data: ${cmd}\n\n`); + }; + ctx.req.on('close', () => { + sendCommand = null; + }); + }); + + let html = ''; + router.get('/', (ctx) => { + ctx.body = html; + }); + + koa.use(router.routes()).use(router.allowedMethods()); + koa.listen(port, '0.0.0.0'); + + return { + name: 'dev-server', + watchChange() { + sendCommand?.('update'); + }, + async generateBundle(_, bundle) { + let body = ''; + Object.values(bundle).forEach((file) => { + if ( + path.extname(file.fileName) === '.html' && + !file.fileName.endsWith('back.html') + ) { + body += file.source; + } + }); + body += ``; + html = ` + +${body} +`; + sendCommand?.('refresh'); + }, + }; +}; + +export default devServer; diff --git a/build/plugins/dev-server/runtime.js b/build/plugins/dev-server/runtime.js new file mode 100644 index 0000000..f012487 --- /dev/null +++ b/build/plugins/dev-server/runtime.js @@ -0,0 +1,12 @@ +const source = new EventSource('/__dev_server'); + +source.onmessage = (event) => { + switch (event.data) { + case 'refresh': + location.reload(); + break; + case 'update': + console.log('code updated. Will refresh after build.'); + break; + } +}; diff --git a/build/plugins/generate-template.js b/build/plugins/generate-template.js new file mode 100644 index 0000000..8018083 --- /dev/null +++ b/build/plugins/generate-template.js @@ -0,0 +1,22 @@ +import { extname } from 'node:path'; + +export default () => ({ + name: 'generate-template', + generateBundle(_, bundle) { + Object.keys(bundle) + .filter((fileName) => extname(fileName) !== '.html') + .forEach((fileName) => { + delete bundle[fileName]; + }); + this.emitFile({ + type: 'asset', + fileName: `style.css`, + source: ``, + }); + this.emitFile({ + type: 'asset', + fileName: `back.html`, + source: `
{{FrontSide}}`, + }); + }, +}); diff --git a/build/rollup.js b/build/rollup.js index 480bf9a..53f3bf3 100644 --- a/build/rollup.js +++ b/build/rollup.js @@ -1,3 +1,5 @@ +import devServer from './plugins/dev-server/index.js'; +import generateTemplate from './plugins/generate-template.js'; import { readJson, ensureValue } from './utils.js'; import alias from '@rollup/plugin-alias'; import commonjs from '@rollup/plugin-commonjs'; @@ -12,7 +14,7 @@ import { dataToEsm } from '@rollup/pluginutils'; import autoprefixer from 'autoprefixer'; import cssnano from 'cssnano'; import fs from 'node:fs/promises'; -import { extname, resolve } from 'node:path'; +import { resolve } from 'node:path'; import postcss from 'rollup-plugin-postcss'; import { swc } from 'rollup-plugin-swc3'; import { visualizer } from 'rollup-plugin-visualizer'; @@ -109,6 +111,26 @@ export async function rollupOptions(config) { }), url(), visualizer(), + html({ + fileName: `front.html`, + template({ files }) { + let frontHtml = ''; + frontHtml += `
`; + frontHtml += ``; + frontHtml += ` + +`; + frontHtml += + files.js + ?.map(({ code }) => ``) + .join('') || ''; + return frontHtml; + }, + }), + generateTemplate(), + envValue(false, () => devServer()), ], onwarn(warning, warn) { if (warning.code === 'MODULE_LEVEL_DIRECTIVE') { @@ -133,44 +155,6 @@ export async function rollupOptions(config) { }), false, ), - html({ - fileName: `front.html`, - template({ files }) { - let frontHtml = ''; - frontHtml += `
`; - frontHtml += ``; - frontHtml += ` - -`; - frontHtml += - files.js - ?.map(({ code }) => ``) - .join('') || ''; - return frontHtml; - }, - }), - { - name: 'generate-template', - generateBundle(_, bundle) { - Object.keys(bundle) - .filter((fileName) => extname(fileName) !== '.html') - .forEach((fileName) => { - delete bundle[fileName]; - }); - this.emitFile({ - type: 'asset', - fileName: `style.css`, - source: ``, - }); - this.emitFile({ - type: 'asset', - fileName: `back.html`, - source: `
{{FrontSide}}`, - }); - }, - }, ], }; } diff --git a/package.json b/package.json index 45a91f9..87d5925 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "devDependencies": { "@changesets/cli": "^2.27.10", "@eslint/js": "^9.9.1", + "@koa/router": "^13.1.0", "@rollup/plugin-alias": "^5.1.0", "@rollup/plugin-commonjs": "^26.0.1", "@rollup/plugin-html": "^1.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7e0b87d..6bf7a08 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,6 +42,9 @@ importers: '@eslint/js': specifier: ^9.9.1 version: 9.9.1 + '@koa/router': + specifier: ^13.1.0 + version: 13.1.0 '@rollup/plugin-alias': specifier: ^5.1.0 version: 5.1.0(rollup@4.21.2) @@ -366,6 +369,10 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@koa/router@13.1.0': + resolution: {integrity: sha512-mNVu1nvkpSd8Q8gMebGbCkDWJ51ODetrFvLKYusej+V0ByD4btqHYnPIzTBLXnQMVUlm/oxVwqmWBY3zQfZilw==} + engines: {node: '>= 18'} + '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} @@ -1563,6 +1570,10 @@ packages: resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} engines: {node: '>= 0.6'} + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + human-id@1.0.2: resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} @@ -2127,6 +2138,9 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -2918,6 +2932,10 @@ packages: resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} engines: {node: '>= 0.6'} + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + string-hash@1.1.3: resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==} @@ -3543,6 +3561,12 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@koa/router@13.1.0': + dependencies: + http-errors: 2.0.0 + koa-compose: 4.1.0 + path-to-regexp: 6.3.0 + '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.25.6 @@ -4882,6 +4906,14 @@ snapshots: statuses: 1.5.0 toidentifier: 1.0.1 + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + human-id@1.0.2: {} hyphenate-style-name@1.1.0: {} @@ -5395,6 +5427,8 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + path-to-regexp@6.3.0: {} + path-type@4.0.0: {} picocolors@1.1.0: {} @@ -6122,6 +6156,8 @@ snapshots: statuses@1.5.0: {} + statuses@2.0.1: {} + string-hash@1.1.3: {} string-width@4.2.3: