From b8f1c9f048c23cff5cd5516b14242d3d3afb618d Mon Sep 17 00:00:00 2001 From: silverwind Date: Sat, 6 Sep 2025 14:58:25 +0200 Subject: [PATCH] Migrate tools and configs to typescript, require node.js >= 22.18.0 (#35421) Migrate all JS config and tools to TS and fix a number of type issues. This required Node.js 22.18.0 or greater where [type-stripping was enabled](https://nodejs.org/en/blog/release/v22.18.0) by default. Given that Node 22 is the current LTS, I think it's ok to assume that the user has a recent version of it. Webpack currently requires the `--disable-interpret` flag to work, should be fixed eventually with https://github.com/webpack/webpack-cli/issues/4525. `fast-glob` is replaced by `fs.globSync`, available in Node 22.0.0 or greater. --- Dockerfile | 3 ++ Dockerfile.rootless | 3 ++ Makefile | 16 +++---- package.json | 6 +-- pnpm-lock.yaml | 17 ------- stylelint.config.js => stylelint.config.ts | 8 ++-- tailwind.config.js => tailwind.config.ts | 13 ++--- ...{generate-images.js => generate-images.ts} | 16 +++---- tools/{generate-svg.js => generate-svg.ts} | 48 ++++++++++--------- ...templates-svg.js => lint-templates-svg.ts} | 7 ++- tsconfig.json | 1 + types.d.ts | 9 ++++ updates.config.js => updates.config.ts | 4 +- webpack.config.js => webpack.config.ts | 37 ++++++-------- 14 files changed, 89 insertions(+), 99 deletions(-) rename stylelint.config.js => stylelint.config.ts (97%) rename tailwind.config.js => tailwind.config.ts (93%) rename tools/{generate-images.js => generate-images.ts} (87%) rename tools/{generate-svg.js => generate-svg.ts} (72%) rename tools/{lint-templates-svg.js => lint-templates-svg.ts} (75%) create mode 100644 types.d.ts rename updates.config.js => updates.config.ts (93%) rename webpack.config.js => webpack.config.ts (90%) diff --git a/Dockerfile b/Dockerfile index 78a556497a..0206840c87 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,9 @@ RUN apk --no-cache add \ && npm install -g pnpm@10 \ && rm -rf /var/cache/apk/* +# workaround for node >= 22.18.0 on alpine 3.22. Remove when upgrading to alpine 3.23 +COPY --from=docker.io/node:22-alpine3.22 /usr/local/bin/node /usr/local/bin/node + # Setup repo COPY . ${GOPATH}/src/code.gitea.io/gitea WORKDIR ${GOPATH}/src/code.gitea.io/gitea diff --git a/Dockerfile.rootless b/Dockerfile.rootless index e83c1af33b..4527018d4c 100644 --- a/Dockerfile.rootless +++ b/Dockerfile.rootless @@ -18,6 +18,9 @@ RUN apk --no-cache add \ && npm install -g pnpm@10 \ && rm -rf /var/cache/apk/* +# workaround for node >= 22.18.0 on alpine 3.22. Remove when upgrading to alpine 3.23 +COPY --from=docker.io/node:22-alpine3.22 /usr/local/bin/node /usr/local/bin/node + # Setup repo COPY . ${GOPATH}/src/code.gitea.io/gitea WORKDIR ${GOPATH}/src/code.gitea.io/gitea diff --git a/Makefile b/Makefile index 793e60acb2..aeebf09e47 100644 --- a/Makefile +++ b/Makefile @@ -127,7 +127,7 @@ GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/m MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) WEBPACK_SOURCES := $(shell find web_src/js web_src/css -type f) -WEBPACK_CONFIGS := webpack.config.js tailwind.config.js +WEBPACK_CONFIGS := webpack.config.ts tailwind.config.ts WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts @@ -153,9 +153,9 @@ TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(DIST) GO_DIRS := build cmd models modules routers services tests WEB_DIRS := web_src/js web_src/css -ESLINT_FILES := web_src/js tools *.js *.ts *.cjs tests/e2e +ESLINT_FILES := web_src/js tools *.ts *.cjs tests/e2e STYLELINT_FILES := web_src/css web_src/js/components/*.vue -SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.js *.md *.yml *.yaml *.toml)) $(filter-out tools/misspellings.csv, $(wildcard tools/*)) +SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.md *.yml *.yaml *.toml)) $(filter-out tools/misspellings.csv, $(wildcard tools/*)) EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini GO_SOURCES := $(wildcard *.go) @@ -407,7 +407,7 @@ lint-actions: ## lint action workflow files .PHONY: lint-templates lint-templates: .venv node_modules ## lint template files - @node tools/lint-templates-svg.js + @node tools/lint-templates-svg.ts @uv run --frozen djlint $(shell find templates -type f -iname '*.tmpl') .PHONY: lint-yaml @@ -421,7 +421,7 @@ watch: ## watch everything and continuously rebuild .PHONY: watch-frontend watch-frontend: node-check node_modules ## watch frontend files and continuously rebuild @rm -rf $(WEBPACK_DEST_ENTRIES) - NODE_ENV=development pnpm exec webpack --watch --progress + NODE_ENV=development pnpm exec webpack --watch --progress --disable-interpret .PHONY: watch-backend watch-backend: go-check ## watch backend files and continuously rebuild @@ -877,13 +877,13 @@ $(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) pnpm-lock.yaml @$(MAKE) -s node-check node_modules @rm -rf $(WEBPACK_DEST_ENTRIES) @echo "Running webpack..." - @BROWSERSLIST_IGNORE_OLD_DATA=true pnpm exec webpack + @BROWSERSLIST_IGNORE_OLD_DATA=true pnpm exec webpack --disable-interpret @touch $(WEBPACK_DEST) .PHONY: svg svg: node-check | node_modules ## build svg files rm -rf $(SVG_DEST_DIR) - node tools/generate-svg.js + node tools/generate-svg.ts .PHONY: svg-check svg-check: svg @@ -922,7 +922,7 @@ generate-gitignore: ## update gitignore files .PHONY: generate-images generate-images: | node_modules ## generate images - cd tools && node generate-images.js $(TAGS) + cd tools && node generate-images.ts $(TAGS) .PHONY: generate-manpage generate-manpage: ## generate manpage diff --git a/package.json b/package.json index 4748946aa7..24dc018bf0 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,8 @@ "type": "module", "packageManager": "pnpm@10.0.0", "engines": { - "node": ">= 20.0.0", - "pnpm": ">=10.0.0" + "node": ">= 22.18.0", + "pnpm": ">= 10.0.0" }, "dependencies": { "@citation-js/core": "0.7.18", @@ -31,7 +31,6 @@ "dropzone": "6.0.0-beta.2", "easymde": "2.20.0", "esbuild-loader": "4.3.0", - "fast-glob": "3.3.3", "htmx.org": "2.0.6", "idiomorph": "0.7.3", "jquery": "3.7.1", @@ -110,7 +109,6 @@ "stylelint-config-recommended": "17.0.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.11", - "stylelint-define-config": "16.22.0", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "4.0.0", "type-fest": "4.41.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a5f877c6d8..ef78142998 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -83,9 +83,6 @@ importers: esbuild-loader: specifier: 4.3.0 version: 4.3.0(webpack@5.101.0) - fast-glob: - specifier: 3.3.3 - version: 3.3.3 htmx.org: specifier: 2.0.6 version: 2.0.6 @@ -315,9 +312,6 @@ importers: stylelint-declaration-strict-value: specifier: 1.10.11 version: 1.10.11(stylelint@16.23.1(typescript@5.8.3)) - stylelint-define-config: - specifier: 16.22.0 - version: 16.22.0(stylelint@16.23.1(typescript@5.8.3)) stylelint-value-no-unknown-custom-properties: specifier: 6.0.1 version: 6.0.1(stylelint@16.23.1(typescript@5.8.3)) @@ -4518,12 +4512,6 @@ packages: peerDependencies: stylelint: '>=7 <=16' - stylelint-define-config@16.22.0: - resolution: {integrity: sha512-EEgHRugsryKo7LpenYyd4yLoZon3lHvRAi7WsMaZoRX9GPOkeDXrMga+N4VA4nK4Zus02EQwyYkndNQ64jaB2A==} - engines: {node: '>=18.0.0', npm: '>=9.0.0', pnpm: '>=8.6.0'} - peerDependencies: - stylelint: '>=16.0.0' - stylelint-value-no-unknown-custom-properties@6.0.1: resolution: {integrity: sha512-N60PTdaTknB35j6D4FhW0GL2LlBRV++bRpXMMldWMQZ240yFQaoltzlLY4lXXs7Z0J5mNUYZQ/gjyVtU2DhCMA==} engines: {node: '>=18.12.0'} @@ -9732,11 +9720,6 @@ snapshots: dependencies: stylelint: 16.23.1(typescript@5.8.3) - stylelint-define-config@16.22.0(stylelint@16.23.1(typescript@5.8.3)): - dependencies: - csstype: 3.1.3 - stylelint: 16.23.1(typescript@5.8.3) - stylelint-value-no-unknown-custom-properties@6.0.1(stylelint@16.23.1(typescript@5.8.3)): dependencies: postcss-value-parser: 4.2.0 diff --git a/stylelint.config.js b/stylelint.config.ts similarity index 97% rename from stylelint.config.js rename to stylelint.config.ts index bd11307b53..8a5b87e17c 100644 --- a/stylelint.config.js +++ b/stylelint.config.ts @@ -1,6 +1,5 @@ -// @ts-check -import {defineConfig} from 'stylelint-define-config'; import {fileURLToPath} from 'node:url'; +import type {Config} from 'stylelint'; const cssVarFiles = [ fileURLToPath(new URL('web_src/css/base.css', import.meta.url)), @@ -8,7 +7,7 @@ const cssVarFiles = [ fileURLToPath(new URL('web_src/css/themes/theme-gitea-dark.css', import.meta.url)), ]; -export default defineConfig({ +export default { extends: 'stylelint-config-recommended', reportUnscopedDisables: true, reportNeedlessDisables: true, @@ -124,7 +123,6 @@ export default defineConfig({ 'csstools/value-no-unknown-custom-properties': [true, {importFrom: cssVarFiles}], 'declaration-block-no-duplicate-properties': [true, {ignore: ['consecutive-duplicates-with-different-values']}], 'declaration-block-no-redundant-longhand-properties': [true, {ignoreShorthands: ['flex-flow', 'overflow', 'grid-template']}], - // @ts-expect-error - https://github.com/stylelint-types/stylelint-define-config/issues/1 'declaration-property-unit-disallowed-list': {'line-height': ['em']}, 'declaration-property-value-disallowed-list': {'word-break': ['break-word']}, 'font-family-name-quotes': 'always-where-recommended', @@ -148,4 +146,4 @@ export default defineConfig({ 'shorthand-property-no-redundant-values': true, 'value-no-vendor-prefix': [true, {ignoreValues: ['box', 'inline-box']}], }, -}); +} satisfies Config; diff --git a/tailwind.config.js b/tailwind.config.ts similarity index 93% rename from tailwind.config.js rename to tailwind.config.ts index fa233a9814..624ae47a5c 100644 --- a/tailwind.config.js +++ b/tailwind.config.ts @@ -2,17 +2,18 @@ import {readFileSync} from 'node:fs'; import {env} from 'node:process'; import {parse} from 'postcss'; import plugin from 'tailwindcss/plugin.js'; +import type {Config} from 'tailwindcss'; const isProduction = env.NODE_ENV !== 'development'; -function extractRootVars(css) { +function extractRootVars(css: string) { const root = parse(css); - const vars = new Set(); + const vars = new Set(); root.walkRules((rule) => { if (rule.selector !== ':root') return; - rule.each((decl) => { - if (decl.value && decl.prop.startsWith('--')) { - vars.add(decl.prop.substring(2)); + rule.each((node) => { + if (node.type === 'decl' && node.value && node.prop.startsWith('--')) { + vars.add(node.prop.substring(2)); } }); }); @@ -120,4 +121,4 @@ export default { }); }), ], -}; +} satisfies Config; diff --git a/tools/generate-images.js b/tools/generate-images.ts similarity index 87% rename from tools/generate-images.js rename to tools/generate-images.ts index 6a4796dd48..6e6ac15486 100755 --- a/tools/generate-images.js +++ b/tools/generate-images.ts @@ -4,12 +4,7 @@ import {optimize} from 'svgo'; import {readFile, writeFile} from 'node:fs/promises'; import {argv, exit} from 'node:process'; -function doExit(err) { - if (err) console.error(err); - exit(err ? 1 : 0); -} - -async function generate(svg, path, {size, bg}) { +async function generate(svg: string, path: string, {size, bg}: {size: number, bg?: boolean}) { const outputFile = new URL(path, import.meta.url); if (String(outputFile).endsWith('.svg')) { @@ -19,7 +14,9 @@ async function generate(svg, path, {size, bg}) { 'removeDimensions', { name: 'addAttributesToSVGElement', - params: {attributes: [{width: size}, {height: size}]}, + params: { + attributes: [{width: String(size)}, {height: String(size)}], + }, }, ], }); @@ -57,7 +54,8 @@ async function main() { } try { - doExit(await main()); + await main(); } catch (err) { - doExit(err); + console.error(err); + exit(1); } diff --git a/tools/generate-svg.js b/tools/generate-svg.ts similarity index 72% rename from tools/generate-svg.js rename to tools/generate-svg.ts index 12f3db039d..b1dc46d451 100755 --- a/tools/generate-svg.js +++ b/tools/generate-svg.ts @@ -1,27 +1,29 @@ #!/usr/bin/env node -import fastGlob from 'fast-glob'; import {optimize} from 'svgo'; -import {parse} from 'node:path'; +import {dirname, parse} from 'node:path'; +import {globSync, writeFileSync} from 'node:fs'; import {readFile, writeFile, mkdir} from 'node:fs/promises'; import {fileURLToPath} from 'node:url'; import {exit} from 'node:process'; -import * as fs from 'node:fs'; +import type {Manifest} from 'material-icon-theme'; -const glob = (pattern) => fastGlob.sync(pattern, { - cwd: fileURLToPath(new URL('..', import.meta.url)), - absolute: true, -}); +const glob = (pattern: string) => globSync(pattern, {cwd: dirname(import.meta.dirname)}); -async function processAssetsSvgFile(file, {prefix, fullName} = {}) { +type Opts = { + prefix?: string, + fullName?: string, +}; + +async function processAssetsSvgFile(path: string, {prefix, fullName}: Opts = {}) { let name = fullName; if (!name) { - name = parse(file).name; + name = parse(path).name; if (prefix) name = `${prefix}-${name}`; if (prefix === 'octicon') name = name.replace(/-[0-9]+$/, ''); // chop of '-16' on octicons } // Set the `xmlns` attribute so that the files are displayable in standalone documents // The svg backend module will strip the attribute during startup for inline display - const {data} = optimize(await readFile(file, 'utf8'), { + const {data} = optimize(await readFile(path, 'utf8'), { plugins: [ {name: 'preset-default'}, {name: 'removeDimensions'}, @@ -41,16 +43,16 @@ async function processAssetsSvgFile(file, {prefix, fullName} = {}) { await writeFile(fileURLToPath(new URL(`../public/assets/img/svg/${name}.svg`, import.meta.url)), data); } -function processAssetsSvgFiles(pattern, opts) { - return glob(pattern).map((file) => processAssetsSvgFile(file, opts)); +function processAssetsSvgFiles(pattern: string, opts: Opts = {}) { + return glob(pattern).map((path) => processAssetsSvgFile(path, opts)); } async function processMaterialFileIcons() { - const files = glob('node_modules/material-icon-theme/icons/*.svg'); - const svgSymbols = {}; - for (const file of files) { + const paths = glob('node_modules/material-icon-theme/icons/*.svg'); + const svgSymbols: Record = {}; + for (const path of paths) { // remove all unnecessary attributes, only keep "viewBox" - const {data} = optimize(await readFile(file, 'utf8'), { + const {data} = optimize(await readFile(path, 'utf8'), { plugins: [ {name: 'preset-default'}, {name: 'removeDimensions'}, @@ -58,16 +60,16 @@ async function processMaterialFileIcons() { {name: 'removeAttrs', params: {attrs: 'xml:space', elemSeparator: ','}}, ], }); - const svgName = parse(file).name; + const svgName = parse(path).name; // intentionally use single quote here to avoid escaping svgSymbols[svgName] = data.replace(/"/g, `'`); } - fs.writeFileSync(fileURLToPath(new URL(`../options/fileicon/material-icon-svgs.json`, import.meta.url)), JSON.stringify(svgSymbols, null, 2)); + writeFileSync(fileURLToPath(new URL(`../options/fileicon/material-icon-svgs.json`, import.meta.url)), JSON.stringify(svgSymbols, null, 2)); - const vscodeExtensionsJson = await readFile(fileURLToPath(new URL(`generate-svg-vscode-extensions.json`, import.meta.url))); - const vscodeExtensions = JSON.parse(vscodeExtensionsJson); - const iconRulesJson = await readFile(fileURLToPath(new URL(`../node_modules/material-icon-theme/dist/material-icons.json`, import.meta.url))); - const iconRules = JSON.parse(iconRulesJson); + const vscodeExtensionsJson = await readFile(fileURLToPath(new URL(`generate-svg-vscode-extensions.json`, import.meta.url)), 'utf8'); + const vscodeExtensions = JSON.parse(vscodeExtensionsJson) as Record; + const iconRulesJson = await readFile(fileURLToPath(new URL(`../node_modules/material-icon-theme/dist/material-icons.json`, import.meta.url)), 'utf8'); + const iconRules = JSON.parse(iconRulesJson) as Manifest; // The rules are from VSCode material-icon-theme, we need to adjust them to our needs // 1. We only use lowercase filenames to match (it should be good enough for most cases and more efficient) // 2. We do not have a "Language ID" system: @@ -91,7 +93,7 @@ async function processMaterialFileIcons() { } } const iconRulesPretty = JSON.stringify(iconRules, null, 2); - fs.writeFileSync(fileURLToPath(new URL(`../options/fileicon/material-icon-rules.json`, import.meta.url)), iconRulesPretty); + writeFileSync(fileURLToPath(new URL(`../options/fileicon/material-icon-rules.json`, import.meta.url)), iconRulesPretty); } async function main() { diff --git a/tools/lint-templates-svg.js b/tools/lint-templates-svg.ts similarity index 75% rename from tools/lint-templates-svg.js rename to tools/lint-templates-svg.ts index 72f756400d..fb8a2c8d58 100755 --- a/tools/lint-templates-svg.js +++ b/tools/lint-templates-svg.ts @@ -1,11 +1,10 @@ #!/usr/bin/env node -import {readdirSync, readFileSync} from 'node:fs'; +import {readdirSync, readFileSync, globSync} from 'node:fs'; import {parse, relative} from 'node:path'; import {fileURLToPath} from 'node:url'; import {exit} from 'node:process'; -import fastGlob from 'fast-glob'; -const knownSvgs = new Set(); +const knownSvgs = new Set(); for (const file of readdirSync(new URL('../public/assets/img/svg', import.meta.url))) { knownSvgs.add(parse(file).name); } @@ -13,7 +12,7 @@ for (const file of readdirSync(new URL('../public/assets/img/svg', import.meta.u const rootPath = fileURLToPath(new URL('..', import.meta.url)); let hadErrors = false; -for (const file of fastGlob.sync(fileURLToPath(new URL('../templates/**/*.tmpl', import.meta.url)))) { +for (const file of globSync(fileURLToPath(new URL('../templates/**/*.tmpl', import.meta.url)))) { const content = readFileSync(file, 'utf8'); for (const [_, name] of content.matchAll(/svg ["'`]([^"'`]+)["'`]/g)) { if (!knownSvgs.has(name)) { diff --git a/tsconfig.json b/tsconfig.json index 4dcee6666f..0a9e080a51 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -41,6 +41,7 @@ "types": [ "vitest/globals", "./web_src/js/globals.d.ts", + "./types.d.ts", ], } } diff --git a/types.d.ts b/types.d.ts new file mode 100644 index 0000000000..d6053697eb --- /dev/null +++ b/types.d.ts @@ -0,0 +1,9 @@ +declare module 'add-asset-webpack-plugin' { + const plugin: any; + export = plugin +} + +declare module '@techknowlogick/license-checker-webpack-plugin' { + const plugin: any; + export = plugin +} diff --git a/updates.config.js b/updates.config.ts similarity index 93% rename from updates.config.js rename to updates.config.ts index 4373ab9b95..633c5de47c 100644 --- a/updates.config.js +++ b/updates.config.ts @@ -1,3 +1,5 @@ +import type {Config} from 'updates'; + export default { exclude: [ '@mcaptcha/vanilla-glue', // breaking changes in rc versions need to be handled @@ -11,4 +13,4 @@ export default { 'eslint-plugin-vitest', // need to migrate to eslint flat config first 'tailwindcss', // need to migrate ], -}; +} satisfies Config; diff --git a/webpack.config.js b/webpack.config.ts similarity index 90% rename from webpack.config.js rename to webpack.config.ts index 70c0e76425..9d3020b0c7 100644 --- a/webpack.config.js +++ b/webpack.config.ts @@ -1,4 +1,3 @@ -import fastGlob from 'fast-glob'; import wrapAnsi from 'wrap-ansi'; import AddAssetPlugin from 'add-asset-webpack-plugin'; import LicenseCheckerWebpackPlugin from '@techknowlogick/license-checker-webpack-plugin'; @@ -6,28 +5,23 @@ import MiniCssExtractPlugin from 'mini-css-extract-plugin'; import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin'; import {VueLoaderPlugin} from 'vue-loader'; import EsBuildLoader from 'esbuild-loader'; -import {parse, dirname} from 'node:path'; -import webpack from 'webpack'; +import {parse} from 'node:path'; +import webpack, {type Configuration, type EntryObject} from 'webpack'; import {fileURLToPath} from 'node:url'; -import {readFileSync} from 'node:fs'; +import {readFileSync, globSync} from 'node:fs'; import {env} from 'node:process'; import tailwindcss from 'tailwindcss'; -import tailwindConfig from './tailwind.config.js'; +import tailwindConfig from './tailwind.config.ts'; import tailwindcssNesting from 'tailwindcss/nesting/index.js'; import postcssNesting from 'postcss-nesting'; const {EsbuildPlugin} = EsBuildLoader; const {SourceMapDevToolPlugin, DefinePlugin, EnvironmentPlugin} = webpack; -const formatLicenseText = (licenseText) => wrapAnsi(licenseText || '', 80).trim(); +const formatLicenseText = (licenseText: string) => wrapAnsi(licenseText || '', 80).trim(); -const glob = (pattern) => fastGlob.sync(pattern, { - cwd: dirname(fileURLToPath(new URL(import.meta.url))), - absolute: true, -}); - -const themes = {}; -for (const path of glob('web_src/css/themes/*.css')) { - themes[parse(path).name] = [path]; +const themes: EntryObject = {}; +for (const path of globSync('web_src/css/themes/*.css', {cwd: import.meta.dirname})) { + themes[parse(path).name] = [`./${path}`]; } const isProduction = env.NODE_ENV !== 'development'; @@ -55,12 +49,12 @@ const webComponents = new Set([ 'text-expander', ]); -const filterCssImport = (url, ...args) => { +const filterCssImport = (url: string, ...args: Array) => { const cssFile = args[1] || args[0]; // resourcePath is 2nd argument for url and 3rd for import const importedFile = url.replace(/[?#].+/, '').toLowerCase(); if (cssFile.includes('fomantic')) { - if (/brand-icons/.test(importedFile)) return false; + if (importedFile.includes('brand-icons')) return false; if (/(eot|ttf|otf|woff|svg)$/i.test(importedFile)) return false; } @@ -71,7 +65,6 @@ const filterCssImport = (url, ...args) => { return true; }; -/** @type {import("webpack").Configuration} */ export default { mode: isProduction ? 'production' : 'development', entry: { @@ -100,7 +93,7 @@ export default { path: fileURLToPath(new URL('public/assets', import.meta.url)), filename: () => 'js/[name].js', chunkFilename: ({chunk}) => { - const language = (/monaco.*languages?_.+?_(.+?)_/.exec(chunk.id) || [])[1]; + const language = (/monaco.*languages?_.+?_(.+?)_/.exec(String(chunk.id)) || [])[1]; return `js/${language ? `monaco-language-${language.toLowerCase()}` : `[name]`}.[contenthash:8].js`; }, }, @@ -129,7 +122,7 @@ export default { loader: 'vue-loader', options: { compilerOptions: { - isCustomElement: (tag) => webComponents.has(tag), + isCustomElement: (tag: string) => webComponents.has(tag), }, }, }, @@ -225,10 +218,10 @@ export default { }), isProduction ? new LicenseCheckerWebpackPlugin({ outputFilename: 'licenses.txt', - outputWriter: ({dependencies}) => { + outputWriter: ({dependencies}: {dependencies: Array>}) => { const line = '-'.repeat(80); const goJson = readFileSync('assets/go-licenses.json', 'utf8'); - const goModules = JSON.parse(goJson).map(({name, licenseText}) => { + const goModules = JSON.parse(goJson).map(({name, licenseText}: Record) => { return {name, body: formatLicenseText(licenseText)}; }); const jsModules = dependencies.map(({name, version, licenseName, licenseText}) => { @@ -285,4 +278,4 @@ export default { reasons: false, runtimeModules: false, }, -}; +} satisfies Configuration;