From 50f1ac92784240c8e6f8df9bc753735c426df273 Mon Sep 17 00:00:00 2001 From: undefined Date: Thu, 29 Aug 2024 22:10:06 +0800 Subject: [PATCH] ui: remove gulp --- .github/workflows/build.yml | 2 +- framework/register/index.js | 22 ++++- framework/register/package.json | 2 +- package.json | 18 ++--- packages/ui-default/build/config/gulp.js | 81 ------------------- packages/ui-default/build/main.ts | 56 ++++++------- .../ui-default/build/plugins/gulpNunjucks.js | 27 ------- .../ui-default/build/plugins/gulpTouch.js | 35 -------- .../components/form/select.page.styl | 2 +- .../misc/icons/template/webicon.inc.styl | 7 +- .../misc/icons/template/webicon.styl | 17 ++-- packages/ui-default/package.json | 51 +++++------- 12 files changed, 87 insertions(+), 233 deletions(-) delete mode 100644 packages/ui-default/build/config/gulp.js delete mode 100644 packages/ui-default/build/plugins/gulpNunjucks.js delete mode 100644 packages/ui-default/build/plugins/gulpTouch.js diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 80f4e4a11..4789e9e88 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,7 +62,7 @@ jobs: run: | if [[ ${{ steps.ui-changed-files.outputs.any_changed }} == true ]] || [[ $(npm info @hydrooj/ui-default version) != $(node -e 'console.log(require("./packages/ui-default/package.json").version)') ]] then - yarn build:ui:gulp + yarn build:ui:iconfont parallel --tty -j+0 yarn ::: lint:ci lint:ui:ci build build:ui:production:webpack test else parallel --tty -j+0 yarn ::: lint:ci build test diff --git a/framework/register/index.js b/framework/register/index.js index d7ee5f518..469fd8dc1 100644 --- a/framework/register/index.js +++ b/framework/register/index.js @@ -21,9 +21,13 @@ process.env.NODE_APP_INSTANCE ||= '0'; const major = +process.version.split('.')[0].split('v')[1]; const minor = +process.version.split('.')[1]; -function transform(filename, tsx = true) { - const code = fs.readFileSync(filename, 'utf-8'); - const result = esbuild.transformSync(code, { +const remove = [ + /(const|let|var)\s+__filename\s*=\s*fileURLToPath\s*\(\s*import\.meta\.url\s*\)\s*;?/g, + /(const|let|var)\s+__dirname\s*=\s*(path\.)?dirname\s*\(\s*__filename\s*\)\s*;?/g, +]; +function tryTransform(filename, content, tsx = true) { + for (const regex of remove) content = content.replace(regex, ''); + return esbuild.transformSync(content, { tsconfigRaw: '{"compilerOptions":{"experimentalDecorators":true}}', sourcefile: filename, sourcemap: 'both', @@ -32,6 +36,18 @@ function transform(filename, tsx = true) { target: `node${major}.${minor}`, jsx: 'transform', }); +} + +function transform(filename, tsx = true) { + const code = fs.readFileSync(filename, 'utf-8'); + let result; + try { + result = tryTransform(filename, code, tsx); + } catch (e) { + if (!e.message.includes('Top-level await')) throw e; + result = tryTransform(filename, code.replace(/await import *\(/g, 'require('), tsx); + console.warn('transforming top-level await to require for file ', filename); + } if (result.warnings.length) console.warn(result.warnings); map[filename] = result.map; if (process.env.LOADER_DUMP_CODE && filename.endsWith(`/${process.env.LOADER_DUMP_CODE}`)) { diff --git a/framework/register/package.json b/framework/register/package.json index 05e97231a..8a7f9dacb 100644 --- a/framework/register/package.json +++ b/framework/register/package.json @@ -1,6 +1,6 @@ { "name": "@hydrooj/register", - "version": "1.0.2", + "version": "1.0.3", "license": "MIT", "main": "index.js", "engines": { diff --git a/package.json b/package.json index 5c2b93839..a52d257e2 100644 --- a/package.json +++ b/package.json @@ -12,11 +12,11 @@ "build": "node build/prepare.js && tsc -b --verbose", "build:installer": "esbuild ./install/install.js --minify --format=cjs | base64", "build:watch": "node build/prepare.js && tsc -b --watch", - "build:ui": "node packages/ui-default/build --gulp && node packages/ui-default/build", - "build:ui:gulp": "node packages/ui-default/build --gulp", - "build:ui:dev": "node packages/ui-default/build --gulp && node --trace-deprecation packages/ui-default/build --dev", - "build:ui:dev:https": "node packages/ui-default/build --gulp && node --trace-deprecation packages/ui-default/build --dev --https", - "build:ui:production": "cross-env NODE_OPTIONS=--max_old_space_size=8192 node packages/ui-default/build --gulp && node packages/ui-default/build --production", + "build:ui": "node packages/ui-default/build --iconfont && node packages/ui-default/build", + "build:ui:iconfont": "node packages/ui-default/build --iconfont", + "build:ui:dev": "node packages/ui-default/build --iconfont && node --trace-deprecation packages/ui-default/build --dev", + "build:ui:dev:https": "node packages/ui-default/build --iconfont && node --trace-deprecation packages/ui-default/build --dev --https", + "build:ui:production": "cross-env NODE_OPTIONS=--max_old_space_size=8192 node packages/ui-default/build --iconfont && node packages/ui-default/build --production", "build:ui:production:webpack": "cross-env NODE_OPTIONS=--max_old_space_size=8192 node packages/ui-default/build --production", "test": "node -r @hydrooj/register packages/utils/tests/subtask.spec.ts && node test/entry.js", "benchmark": "cross-env BENCHMARK=true node test/entry.js", @@ -35,7 +35,7 @@ "devDependencies": { "@simplewebauthn/types": "9.0.1", "@types/autocannon": "^7.12.5", - "@types/chai": "^4.3.17", + "@types/chai": "^4.3.19", "@types/cross-spawn": "^6.0.6", "@types/node": "20.12.12", "@types/semver": "^7.5.8", @@ -46,14 +46,14 @@ "cross-spawn": "^7.0.3", "esbuild": "0.23.0", "eslint": "^8.57.0", - "eslint-import-resolver-typescript": "^3.6.1", + "eslint-import-resolver-typescript": "^3.6.3", "eslint-import-resolver-webpack": "^0.13.8", "fs-extra": "^11.2.0", "globby": "^14.0.2", "inspectpack": "^4.7.1", - "mongodb-memory-server": "^10.0.0", + "mongodb-memory-server": "9.4.1", "nyc": "^17.0.0", - "ora": "^8.0.1", + "ora": "^8.1.0", "package-json": "^10.0.1", "semver": "^7.6.3", "simple-git": "^3.25.0", diff --git a/packages/ui-default/build/config/gulp.js b/packages/ui-default/build/config/gulp.js deleted file mode 100644 index 7610719e4..000000000 --- a/packages/ui-default/build/config/gulp.js +++ /dev/null @@ -1,81 +0,0 @@ -/* eslint-disable no-restricted-syntax */ -/* eslint-disable guard-for-in */ -/* eslint-disable import/no-extraneous-dependencies */ -import chalk from 'chalk'; -import log from 'fancy-log'; -import gulp from 'gulp'; -import iconfont from 'gulp-iconfont'; -import gulpif from 'gulp-if'; -import plumber from 'gulp-plumber'; -import svgmin from 'gulp-svgmin'; -import _ from 'lodash'; -import vinylBuffer from 'vinyl-buffer'; -import nunjucks from '../plugins/gulpNunjucks'; -import vjTouch from '../plugins/gulpTouch'; - -let isInWatchMode = false; -export const tasks = {}; -const iconTimestamp = 1577836800; // 2020-1-1 - -function handleWatchChange(name, r = 300) { - return _.debounce((ev) => { - log('File %s: %s', chalk.yellow(ev.type), ev.path); - tasks[name](); - }, r); -} - -function offsetMtimeAtFirstBuild() { - // Offset the mtime at first build to - // workaround webpack/watchpack issue #25. - return gulpif(!isInWatchMode, vjTouch(~~((Date.now() - 30 * 1000) / 1000))); -} - -export default function ({ errorHandler }) { - let iconfontTemplateArgs = null; - - tasks['iconfont:template'] = () => gulp - .src('misc/icons/template/*.styl') - .pipe(nunjucks(iconfontTemplateArgs)) - .pipe(gulp.dest('misc/.iconfont')) - .pipe(offsetMtimeAtFirstBuild()); - - tasks.iconfont = () => gulp - .src('misc/icons/*.svg') - .pipe(plumber({ errorHandler })) - .pipe(svgmin()) - .pipe(gulp.dest('misc/icons')) - .pipe(offsetMtimeAtFirstBuild()) - .pipe(iconfont({ - fontHeight: 1000, - prependUnicode: false, - descent: 6.25 / 100 * 1000, - fontName: 'vj4icon', - formats: ['svg', 'ttf', 'eot', 'woff', 'woff2'], - timestamp: iconTimestamp, - })) - .on('glyphs', (glyphs, options) => { - iconfontTemplateArgs = { glyphs, options }; - tasks['iconfont:template'](); - }) - .pipe(gulp.dest('misc/.iconfont')) - .pipe(vinylBuffer()) - .pipe(offsetMtimeAtFirstBuild()); - - tasks.watch = () => { - isInWatchMode = true; - gulp.watch('misc/icons/*.svg', handleWatchChange('iconfont')); - }; - - for (const key in tasks) { - // gulp4 uses function name directly as task name - Object.defineProperty(tasks[key], 'name', { - value: key, - configurable: true, - }); - tasks[key] = gulp.series(tasks[key]); - } - - tasks.default = gulp.series(gulp.parallel(tasks.iconfont)); - - return tasks; -} diff --git a/packages/ui-default/build/main.ts b/packages/ui-default/build/main.ts index d0c251daa..613a1364b 100644 --- a/packages/ui-default/build/main.ts +++ b/packages/ui-default/build/main.ts @@ -2,15 +2,13 @@ import { size } from '@hydrooj/utils/lib/utils'; import cac from 'cac'; import chalk from 'chalk'; -import log from 'fancy-log'; import fs from 'fs-extra'; import { globbySync } from 'globby'; -import gulp from 'gulp'; import path from 'path'; +import svgtofont from 'svgtofont'; import webpack, { Stats } from 'webpack'; import WebpackDevServer from 'webpack-dev-server'; import pkg from '../package.json'; -import gulpConfig from './config/gulp'; import webpackConfig from './config/webpack'; import root from './utils/root'; @@ -66,14 +64,15 @@ async function runWebpack({ const data = await fs.stat(path.join(root('public'), file.name)); const key = file.name .replace(/\.[a-f0-9]{6}\.chunk\./, '.chunk.') - .replace(/\.[a-f0-9]{6}\.worker\./, '.worker.'); + .replace(/\.[a-f0-9]{6}\.worker\./, '.worker.') + .replace(/\.[a-f0-9]{6}\.e\.js/, '.e.js'); stats[key] = data.size; } const statsPath = root('__bundleInfo'); let oldTotal = 0; let newTotal = 0; if (fs.existsSync(statsPath)) { - log('Compare to last production bundle:'); + console.log('Compare to last production bundle:'); const oldStats = JSON.parse(await fs.readFile(statsPath, 'utf-8')) as Record; for (const key in stats) oldStats[key] ||= 0; const entries: [filename: string, orig: number, curr: number][] = []; @@ -89,40 +88,37 @@ async function runWebpack({ const diff = 100 * (curr - orig) / orig; if (Math.abs(diff) < 0.01 && name !== 'Total') continue; const color = orig > curr ? chalk.green : chalk.red; - log(color(`${name.padStart(35)} ${size(orig).padStart(10)} -> ${size(curr).padEnd(10)} (${diff.toPrecision(5)}%)`), chalk.reset()); + console.log(color(`${name.padStart(35)} ${size(orig).padStart(10)} -> ${size(curr).padEnd(10)} (${diff.toPrecision(5)}%)`), chalk.reset()); } } await fs.writeFile(statsPath, JSON.stringify(stats)); } } -async function runGulp() { - function errorHandler(err) { - log(chalk.red('Error: %s'), chalk.reset(err.toString() + err.stack)); - process.exit(1); - } - const gulpTasks = gulpConfig({ errorHandler }); - return new Promise((resolve) => { - const taskList = {}; - - gulp.on('start', ({ uid, name }) => { - log(chalk.blue('Starting task: %s'), chalk.reset(name)); - taskList[uid] = true; - }); - gulp.on('stop', ({ uid, name }) => { - log(chalk.green('Finished: %s'), chalk.reset(name)); - taskList[uid] = false; - if (Object.values(taskList).filter((b) => b).length === 0) resolve(null); - }); - gulpTasks.default(); - }); -} - async function main() { const dir = process.cwd(); process.chdir(root()); - if (argv.options.gulp) await runGulp(); - else { + if (argv.options.iconfont) { + await svgtofont({ + src: root('misc/icons'), + dist: root('misc/.iconfont'), + styleTemplates: root('misc/icons/template'), + classNamePrefix: 'icon', + fontName: 'hydro-icons', + css: true, + startUnicode: 0xea01, + svg2ttf: { + timestamp: 1577836800, // 2020-1-1 + }, + svgicons2svgfont: { + fontHeight: 1000, + descent: 6.25 / 100 * 1000, + normalize: true, + }, + }); + const file = fs.readFileSync(root('misc/.iconfont/webicon.inc.styl'), 'utf-8'); + fs.writeFileSync(root('misc/.iconfont/webicon.inc.styl'), file.replace(/:/g, ' =')); + } else { await runWebpack(argv.options as any); if (fs.existsSync('public/theme.css')) { fs.copyFileSync('public/theme.css', `public/theme-${pkg.version}.css`); diff --git a/packages/ui-default/build/plugins/gulpNunjucks.js b/packages/ui-default/build/plugins/gulpNunjucks.js deleted file mode 100644 index e1556b2fe..000000000 --- a/packages/ui-default/build/plugins/gulpNunjucks.js +++ /dev/null @@ -1,27 +0,0 @@ -import nunjucks from 'nunjucks'; -import through from 'through2'; - -export default function compile(data, options = {}) { - return through.obj(function (file, encoding, callback) { - if (file.isNull()) { - callback(null, file); - return; - } - const context = { ...data, ...file.data }; - const filePath = file.path; - const env = options.env || new nunjucks.Environment(new nunjucks.FileSystemLoader(file.base), options); - if (options.filters && !options.env) { - for (const key of Object.keys(options.filters)) { - env.addFilter(key, options.filters[key]); - } - } - try { - file.contents = Buffer.from(env.renderString(file.contents.toString(), context)); - file.extname = '.styl'; - this.push(file); - } catch (error) { - this.emit('error', new Error(`gulp-nunjucks ${error} ${filePath}`)); - } - callback(); - }); -} diff --git a/packages/ui-default/build/plugins/gulpTouch.js b/packages/ui-default/build/plugins/gulpTouch.js deleted file mode 100644 index 58fc26a8e..000000000 --- a/packages/ui-default/build/plugins/gulpTouch.js +++ /dev/null @@ -1,35 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ -import fs from 'fs-extra'; -import PluginError from 'plugin-error'; -import through from 'through2'; - -export default function touch(mtime) { - async function touchFile(file) { - const fd = await fs.open(file.path, 'a'); - try { - await fs.futimes(fd, mtime, mtime); - } finally { - await fs.close(fd); - } - } - - function processStream(file, encoding, callback) { - if (file.isNull()) { - callback(); - return; - } - if (file.isStream()) { - this.emit('error', new PluginError('gulpTouch', 'Stream not supported')); - callback(); - return; - } - touchFile(file) - .catch((err) => this.emit('error', err)) - .then(() => { - this.push(file); - callback(); - }); - } - - return through.obj(processStream); -} diff --git a/packages/ui-default/components/form/select.page.styl b/packages/ui-default/components/form/select.page.styl index 125cdf03f..d96bffd81 100644 --- a/packages/ui-default/components/form/select.page.styl +++ b/packages/ui-default/components/form/select.page.styl @@ -4,7 +4,7 @@ form-styles() padding-right: 1.1rem background-color: $input-background-color - background-image: url('~vj/misc/icons/' + $icon-expand_more-file) + background-image: url('~vj/misc/icons/expand_more.svg') background-size: 16px 16px background-position: right -1rem center background-origin: content-box diff --git a/packages/ui-default/misc/icons/template/webicon.inc.styl b/packages/ui-default/misc/icons/template/webicon.inc.styl index 57861feff..b8db30270 100644 --- a/packages/ui-default/misc/icons/template/webicon.inc.styl +++ b/packages/ui-default/misc/icons/template/webicon.inc.styl @@ -1,6 +1,3 @@ -$icon-font-name = '{{ options.fontName }}' +$icon-font-name = 'hydro-icons' -{% for icon in glyphs %} -$icon-{{ icon.name }} = '\{{ icon.unicode[0].charCodeAt(0).toString(16)|upper }}' -$icon-{{ icon.name }}-file = '{{ icon.name }}.svg' -{% endfor %} +{{cssToVars}} diff --git a/packages/ui-default/misc/icons/template/webicon.styl b/packages/ui-default/misc/icons/template/webicon.styl index 54504cc16..f623b79ab 100644 --- a/packages/ui-default/misc/icons/template/webicon.styl +++ b/packages/ui-default/misc/icons/template/webicon.styl @@ -1,15 +1,15 @@ @font-face font-family: $icon-font-name - src: url('./{{ options.fontName }}.eot') - src: url('./{{ options.fontName }}.eot#iefix') format('embedded-opentype'), - url('./{{ options.fontName }}.woff2') format('woff2'), - url('./{{ options.fontName }}.woff') format('woff'), - url('./{{ options.fontName }}.ttf') format('truetype') + src: url('./hydro-icons.eot') + src: url('./hydro-icons.eot#iefix') format('embedded-opentype'), + url('./hydro-icons.woff2') format('woff2'), + url('./hydro-icons.woff') format('woff'), + url('./hydro-icons.ttf') format('truetype') font-weight: normal font-style: normal .icon - font-family: $icon-font-name !important + font-family: $icon-font-name !important;{{fontSize}} speak: none font-style: normal font-weight: normal @@ -19,7 +19,4 @@ -webkit-font-smoothing: antialiased -moz-osx-font-smoothing: grayscale -{% for icon in glyphs %} -.icon-{{ icon.name }}:before - content: $icon-{{ icon.name }} -{% endfor %} +{{cssString}} diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 70ef19a76..466343d7d 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -14,22 +14,21 @@ "cli": false }, "devDependencies": { - "@blueprintjs/core": "^5.11.0", + "@blueprintjs/core": "^5.12.0", "@cordisjs/core": "3.17.3", - "@fontsource/dm-mono": "^5.0.20", - "@fontsource/fira-code": "^5.0.18", - "@fontsource/inconsolata": "^5.0.18", - "@fontsource/jetbrains-mono": "^5.0.20", - "@fontsource/pt-mono": "^5.0.13", - "@fontsource/roboto-mono": "^5.0.18", - "@fontsource/source-code-pro": "^5.0.18", - "@fontsource/ubuntu-mono": "^5.0.20", + "@fontsource/dm-mono": "^5.0.21", + "@fontsource/fira-code": "^5.0.19", + "@fontsource/inconsolata": "^5.0.19", + "@fontsource/jetbrains-mono": "^5.0.21", + "@fontsource/pt-mono": "^5.0.14", + "@fontsource/roboto-mono": "^5.0.19", + "@fontsource/source-code-pro": "^5.0.19", + "@fontsource/ubuntu-mono": "^5.0.21", "@hydrooj/utils": "workspace:^", - "@sentry/browser": "^8.26.0", - "@sentry/webpack-plugin": "^2.22.2", + "@sentry/browser": "^8.27.0", + "@sentry/webpack-plugin": "^2.22.3", "@simplewebauthn/browser": "9.0.1", "@svgr/webpack": "^8.1.0", - "@types/gulp-if": "^0.0.34", "@types/jquery": "^3.5.30", "@types/jquery.transit": "^0.9.33", "@types/json-schema": "^7.0.15", @@ -38,7 +37,7 @@ "@types/nunjucks": "^3.2.6", "@types/pickadate": "^3.5.35", "@types/qrcode": "^1.5.5", - "@types/react": "^18.3.3", + "@types/react": "^18.3.4", "@types/react-dom": "^18.3.0", "@types/redux-logger": "^3.0.13", "@types/uuid": "^10.0.0", @@ -53,8 +52,8 @@ "clean-webpack-plugin": "^4.0.0", "clipboard": "^2.0.11", "copy-webpack-plugin": "^12.0.2", - "core-js": "^3.38.0", - "core-js-compat": "^3.38.0", + "core-js": "^3.38.1", + "core-js-compat": "^3.38.1", "css-loader": "^6.11.0", "diff": "^5.2.0", "diff-dom": "^5.1.4", @@ -62,22 +61,16 @@ "emojis-keywords": "2.0.0", "emojis-list": "2.1.0", "esbuild-loader": "^4.2.2", - "fancy-log": "^2.0.0", "flatpickr": "^4.6.13", - "graphiql": "^3.4.1", - "gulp": "^4.0.2", - "gulp-iconfont": "^11.0.1", - "gulp-if": "^3.0.0", - "gulp-plumber": "^1.2.1", - "gulp-svgmin": "^4.1.0", + "graphiql": "^3.7.1", "idb": "^8.0.0", "jquery": "^3.7.1", "jquery-scroll-lock": "^3.1.3", "jquery.easing": "^1.4.1", "jquery.transit": "^0.9.12", "matchmedia-polyfill": "^0.3.2", - "md-editor-rt": "^4.18.1", - "mini-css-extract-plugin": "^2.9.0", + "md-editor-rt": "^4.19.2", + "mini-css-extract-plugin": "^2.9.1", "moment": "^2.30.1", "monaco-editor": "0.50.0", "monaco-editor-nls": "^3.1.0", @@ -89,7 +82,6 @@ "nprogress": "^0.2.0", "p-queue": "^8.0.1", "pickadate": "^3.6.4", - "plugin-error": "^2.0.1", "postcss-loader": "7.3.4", "prism-themes": "^1.9.0", "prismjs": "^1.29.0", @@ -116,15 +108,14 @@ "style-loader": "^3.3.4", "stylus": "^0.63.0", "stylus-loader": "7.1.2", + "svgtofont": "^5.0.3", "tether": "1.4.7", "tether-drop": "^1.4.2", - "through2": "^4.0.2", "timeago-react": "^3.0.6", "timeago.js": "^4.0.2", "ts-loader": "^9.5.1", - "vinyl-buffer": "^1.0.1", "web-streams-polyfill": "^4.0.0", - "webpack": "^5.93.0", + "webpack": "^5.94.0", "webpack-bundle-analyzer": "^4.10.2", "webpack-dev-server": "^5.0.4", "webpack-manifest-plugin": "^5.0.0", @@ -132,7 +123,7 @@ }, "dependencies": { "ansi_up": "^6.0.2", - "cssfilter": "^0.0.10", + "cssfilter": "^0.0.11", "esbuild": "0.23.0", "fs-extra": "^11.2.0", "js-yaml": "^4.1.0", @@ -140,7 +131,7 @@ "katex": "^0.16.11", "lodash": "^4.17.21", "markdown-it": "^14.1.0", - "markdown-it-anchor": "^9.0.1", + "markdown-it-anchor": "^9.1.0", "markdown-it-attrs": "^4.2.0", "markdown-it-footnote": "^4.0.0", "markdown-it-imsize": "^2.0.1",