Skip to content

Commit b8f1c9f

Browse files
authored
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 webpack/webpack-cli#4525. `fast-glob` is replaced by `fs.globSync`, available in Node 22.0.0 or greater.
1 parent 1640e9a commit b8f1c9f

14 files changed

+89
-99
lines changed

Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ RUN apk --no-cache add \
1818
&& npm install -g pnpm@10 \
1919
&& rm -rf /var/cache/apk/*
2020

21+
# workaround for node >= 22.18.0 on alpine 3.22. Remove when upgrading to alpine 3.23
22+
COPY --from=docker.io/node:22-alpine3.22 /usr/local/bin/node /usr/local/bin/node
23+
2124
# Setup repo
2225
COPY . ${GOPATH}/src/code.gitea.io/gitea
2326
WORKDIR ${GOPATH}/src/code.gitea.io/gitea

Dockerfile.rootless

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ RUN apk --no-cache add \
1818
&& npm install -g pnpm@10 \
1919
&& rm -rf /var/cache/apk/*
2020

21+
# workaround for node >= 22.18.0 on alpine 3.22. Remove when upgrading to alpine 3.23
22+
COPY --from=docker.io/node:22-alpine3.22 /usr/local/bin/node /usr/local/bin/node
23+
2124
# Setup repo
2225
COPY . ${GOPATH}/src/code.gitea.io/gitea
2326
WORKDIR ${GOPATH}/src/code.gitea.io/gitea

Makefile

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/m
127127
MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list code.gitea.io/gitea/models/migrations/...)
128128

129129
WEBPACK_SOURCES := $(shell find web_src/js web_src/css -type f)
130-
WEBPACK_CONFIGS := webpack.config.js tailwind.config.js
130+
WEBPACK_CONFIGS := webpack.config.ts tailwind.config.ts
131131
WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css
132132
WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts
133133

@@ -153,9 +153,9 @@ TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(DIST)
153153
GO_DIRS := build cmd models modules routers services tests
154154
WEB_DIRS := web_src/js web_src/css
155155

156-
ESLINT_FILES := web_src/js tools *.js *.ts *.cjs tests/e2e
156+
ESLINT_FILES := web_src/js tools *.ts *.cjs tests/e2e
157157
STYLELINT_FILES := web_src/css web_src/js/components/*.vue
158-
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/*))
158+
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/*))
159159
EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini
160160

161161
GO_SOURCES := $(wildcard *.go)
@@ -407,7 +407,7 @@ lint-actions: ## lint action workflow files
407407

408408
.PHONY: lint-templates
409409
lint-templates: .venv node_modules ## lint template files
410-
@node tools/lint-templates-svg.js
410+
@node tools/lint-templates-svg.ts
411411
@uv run --frozen djlint $(shell find templates -type f -iname '*.tmpl')
412412

413413
.PHONY: lint-yaml
@@ -421,7 +421,7 @@ watch: ## watch everything and continuously rebuild
421421
.PHONY: watch-frontend
422422
watch-frontend: node-check node_modules ## watch frontend files and continuously rebuild
423423
@rm -rf $(WEBPACK_DEST_ENTRIES)
424-
NODE_ENV=development pnpm exec webpack --watch --progress
424+
NODE_ENV=development pnpm exec webpack --watch --progress --disable-interpret
425425

426426
.PHONY: watch-backend
427427
watch-backend: go-check ## watch backend files and continuously rebuild
@@ -877,13 +877,13 @@ $(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) pnpm-lock.yaml
877877
@$(MAKE) -s node-check node_modules
878878
@rm -rf $(WEBPACK_DEST_ENTRIES)
879879
@echo "Running webpack..."
880-
@BROWSERSLIST_IGNORE_OLD_DATA=true pnpm exec webpack
880+
@BROWSERSLIST_IGNORE_OLD_DATA=true pnpm exec webpack --disable-interpret
881881
@touch $(WEBPACK_DEST)
882882

883883
.PHONY: svg
884884
svg: node-check | node_modules ## build svg files
885885
rm -rf $(SVG_DEST_DIR)
886-
node tools/generate-svg.js
886+
node tools/generate-svg.ts
887887

888888
.PHONY: svg-check
889889
svg-check: svg
@@ -922,7 +922,7 @@ generate-gitignore: ## update gitignore files
922922

923923
.PHONY: generate-images
924924
generate-images: | node_modules ## generate images
925-
cd tools && node generate-images.js $(TAGS)
925+
cd tools && node generate-images.ts $(TAGS)
926926

927927
.PHONY: generate-manpage
928928
generate-manpage: ## generate manpage

package.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
"type": "module",
33
"packageManager": "[email protected]",
44
"engines": {
5-
"node": ">= 20.0.0",
6-
"pnpm": ">=10.0.0"
5+
"node": ">= 22.18.0",
6+
"pnpm": ">= 10.0.0"
77
},
88
"dependencies": {
99
"@citation-js/core": "0.7.18",
@@ -31,7 +31,6 @@
3131
"dropzone": "6.0.0-beta.2",
3232
"easymde": "2.20.0",
3333
"esbuild-loader": "4.3.0",
34-
"fast-glob": "3.3.3",
3534
"htmx.org": "2.0.6",
3635
"idiomorph": "0.7.3",
3736
"jquery": "3.7.1",
@@ -110,7 +109,6 @@
110109
"stylelint-config-recommended": "17.0.0",
111110
"stylelint-declaration-block-no-ignored-properties": "2.8.0",
112111
"stylelint-declaration-strict-value": "1.10.11",
113-
"stylelint-define-config": "16.22.0",
114112
"stylelint-value-no-unknown-custom-properties": "6.0.1",
115113
"svgo": "4.0.0",
116114
"type-fest": "4.41.0",

pnpm-lock.yaml

Lines changed: 0 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

stylelint.config.js renamed to stylelint.config.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
// @ts-check
2-
import {defineConfig} from 'stylelint-define-config';
31
import {fileURLToPath} from 'node:url';
2+
import type {Config} from 'stylelint';
43

54
const cssVarFiles = [
65
fileURLToPath(new URL('web_src/css/base.css', import.meta.url)),
76
fileURLToPath(new URL('web_src/css/themes/theme-gitea-light.css', import.meta.url)),
87
fileURLToPath(new URL('web_src/css/themes/theme-gitea-dark.css', import.meta.url)),
98
];
109

11-
export default defineConfig({
10+
export default {
1211
extends: 'stylelint-config-recommended',
1312
reportUnscopedDisables: true,
1413
reportNeedlessDisables: true,
@@ -124,7 +123,6 @@ export default defineConfig({
124123
'csstools/value-no-unknown-custom-properties': [true, {importFrom: cssVarFiles}],
125124
'declaration-block-no-duplicate-properties': [true, {ignore: ['consecutive-duplicates-with-different-values']}],
126125
'declaration-block-no-redundant-longhand-properties': [true, {ignoreShorthands: ['flex-flow', 'overflow', 'grid-template']}],
127-
// @ts-expect-error - https://github.com/stylelint-types/stylelint-define-config/issues/1
128126
'declaration-property-unit-disallowed-list': {'line-height': ['em']},
129127
'declaration-property-value-disallowed-list': {'word-break': ['break-word']},
130128
'font-family-name-quotes': 'always-where-recommended',
@@ -148,4 +146,4 @@ export default defineConfig({
148146
'shorthand-property-no-redundant-values': true,
149147
'value-no-vendor-prefix': [true, {ignoreValues: ['box', 'inline-box']}],
150148
},
151-
});
149+
} satisfies Config;

tailwind.config.js renamed to tailwind.config.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@ import {readFileSync} from 'node:fs';
22
import {env} from 'node:process';
33
import {parse} from 'postcss';
44
import plugin from 'tailwindcss/plugin.js';
5+
import type {Config} from 'tailwindcss';
56

67
const isProduction = env.NODE_ENV !== 'development';
78

8-
function extractRootVars(css) {
9+
function extractRootVars(css: string) {
910
const root = parse(css);
10-
const vars = new Set();
11+
const vars = new Set<string>();
1112
root.walkRules((rule) => {
1213
if (rule.selector !== ':root') return;
13-
rule.each((decl) => {
14-
if (decl.value && decl.prop.startsWith('--')) {
15-
vars.add(decl.prop.substring(2));
14+
rule.each((node) => {
15+
if (node.type === 'decl' && node.value && node.prop.startsWith('--')) {
16+
vars.add(node.prop.substring(2));
1617
}
1718
});
1819
});
@@ -120,4 +121,4 @@ export default {
120121
});
121122
}),
122123
],
123-
};
124+
} satisfies Config;

tools/generate-images.js renamed to tools/generate-images.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,7 @@ import {optimize} from 'svgo';
44
import {readFile, writeFile} from 'node:fs/promises';
55
import {argv, exit} from 'node:process';
66

7-
function doExit(err) {
8-
if (err) console.error(err);
9-
exit(err ? 1 : 0);
10-
}
11-
12-
async function generate(svg, path, {size, bg}) {
7+
async function generate(svg: string, path: string, {size, bg}: {size: number, bg?: boolean}) {
138
const outputFile = new URL(path, import.meta.url);
149

1510
if (String(outputFile).endsWith('.svg')) {
@@ -19,7 +14,9 @@ async function generate(svg, path, {size, bg}) {
1914
'removeDimensions',
2015
{
2116
name: 'addAttributesToSVGElement',
22-
params: {attributes: [{width: size}, {height: size}]},
17+
params: {
18+
attributes: [{width: String(size)}, {height: String(size)}],
19+
},
2320
},
2421
],
2522
});
@@ -57,7 +54,8 @@ async function main() {
5754
}
5855

5956
try {
60-
doExit(await main());
57+
await main();
6158
} catch (err) {
62-
doExit(err);
59+
console.error(err);
60+
exit(1);
6361
}

tools/generate-svg.js renamed to tools/generate-svg.ts

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
11
#!/usr/bin/env node
2-
import fastGlob from 'fast-glob';
32
import {optimize} from 'svgo';
4-
import {parse} from 'node:path';
3+
import {dirname, parse} from 'node:path';
4+
import {globSync, writeFileSync} from 'node:fs';
55
import {readFile, writeFile, mkdir} from 'node:fs/promises';
66
import {fileURLToPath} from 'node:url';
77
import {exit} from 'node:process';
8-
import * as fs from 'node:fs';
8+
import type {Manifest} from 'material-icon-theme';
99

10-
const glob = (pattern) => fastGlob.sync(pattern, {
11-
cwd: fileURLToPath(new URL('..', import.meta.url)),
12-
absolute: true,
13-
});
10+
const glob = (pattern: string) => globSync(pattern, {cwd: dirname(import.meta.dirname)});
1411

15-
async function processAssetsSvgFile(file, {prefix, fullName} = {}) {
12+
type Opts = {
13+
prefix?: string,
14+
fullName?: string,
15+
};
16+
17+
async function processAssetsSvgFile(path: string, {prefix, fullName}: Opts = {}) {
1618
let name = fullName;
1719
if (!name) {
18-
name = parse(file).name;
20+
name = parse(path).name;
1921
if (prefix) name = `${prefix}-${name}`;
2022
if (prefix === 'octicon') name = name.replace(/-[0-9]+$/, ''); // chop of '-16' on octicons
2123
}
2224
// Set the `xmlns` attribute so that the files are displayable in standalone documents
2325
// The svg backend module will strip the attribute during startup for inline display
24-
const {data} = optimize(await readFile(file, 'utf8'), {
26+
const {data} = optimize(await readFile(path, 'utf8'), {
2527
plugins: [
2628
{name: 'preset-default'},
2729
{name: 'removeDimensions'},
@@ -41,33 +43,33 @@ async function processAssetsSvgFile(file, {prefix, fullName} = {}) {
4143
await writeFile(fileURLToPath(new URL(`../public/assets/img/svg/${name}.svg`, import.meta.url)), data);
4244
}
4345

44-
function processAssetsSvgFiles(pattern, opts) {
45-
return glob(pattern).map((file) => processAssetsSvgFile(file, opts));
46+
function processAssetsSvgFiles(pattern: string, opts: Opts = {}) {
47+
return glob(pattern).map((path) => processAssetsSvgFile(path, opts));
4648
}
4749

4850
async function processMaterialFileIcons() {
49-
const files = glob('node_modules/material-icon-theme/icons/*.svg');
50-
const svgSymbols = {};
51-
for (const file of files) {
51+
const paths = glob('node_modules/material-icon-theme/icons/*.svg');
52+
const svgSymbols: Record<string, string> = {};
53+
for (const path of paths) {
5254
// remove all unnecessary attributes, only keep "viewBox"
53-
const {data} = optimize(await readFile(file, 'utf8'), {
55+
const {data} = optimize(await readFile(path, 'utf8'), {
5456
plugins: [
5557
{name: 'preset-default'},
5658
{name: 'removeDimensions'},
5759
{name: 'removeXMLNS'},
5860
{name: 'removeAttrs', params: {attrs: 'xml:space', elemSeparator: ','}},
5961
],
6062
});
61-
const svgName = parse(file).name;
63+
const svgName = parse(path).name;
6264
// intentionally use single quote here to avoid escaping
6365
svgSymbols[svgName] = data.replace(/"/g, `'`);
6466
}
65-
fs.writeFileSync(fileURLToPath(new URL(`../options/fileicon/material-icon-svgs.json`, import.meta.url)), JSON.stringify(svgSymbols, null, 2));
67+
writeFileSync(fileURLToPath(new URL(`../options/fileicon/material-icon-svgs.json`, import.meta.url)), JSON.stringify(svgSymbols, null, 2));
6668

67-
const vscodeExtensionsJson = await readFile(fileURLToPath(new URL(`generate-svg-vscode-extensions.json`, import.meta.url)));
68-
const vscodeExtensions = JSON.parse(vscodeExtensionsJson);
69-
const iconRulesJson = await readFile(fileURLToPath(new URL(`../node_modules/material-icon-theme/dist/material-icons.json`, import.meta.url)));
70-
const iconRules = JSON.parse(iconRulesJson);
69+
const vscodeExtensionsJson = await readFile(fileURLToPath(new URL(`generate-svg-vscode-extensions.json`, import.meta.url)), 'utf8');
70+
const vscodeExtensions = JSON.parse(vscodeExtensionsJson) as Record<string, string>;
71+
const iconRulesJson = await readFile(fileURLToPath(new URL(`../node_modules/material-icon-theme/dist/material-icons.json`, import.meta.url)), 'utf8');
72+
const iconRules = JSON.parse(iconRulesJson) as Manifest;
7173
// The rules are from VSCode material-icon-theme, we need to adjust them to our needs
7274
// 1. We only use lowercase filenames to match (it should be good enough for most cases and more efficient)
7375
// 2. We do not have a "Language ID" system:
@@ -91,7 +93,7 @@ async function processMaterialFileIcons() {
9193
}
9294
}
9395
const iconRulesPretty = JSON.stringify(iconRules, null, 2);
94-
fs.writeFileSync(fileURLToPath(new URL(`../options/fileicon/material-icon-rules.json`, import.meta.url)), iconRulesPretty);
96+
writeFileSync(fileURLToPath(new URL(`../options/fileicon/material-icon-rules.json`, import.meta.url)), iconRulesPretty);
9597
}
9698

9799
async function main() {

tools/lint-templates-svg.js renamed to tools/lint-templates-svg.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
#!/usr/bin/env node
2-
import {readdirSync, readFileSync} from 'node:fs';
2+
import {readdirSync, readFileSync, globSync} from 'node:fs';
33
import {parse, relative} from 'node:path';
44
import {fileURLToPath} from 'node:url';
55
import {exit} from 'node:process';
6-
import fastGlob from 'fast-glob';
76

8-
const knownSvgs = new Set();
7+
const knownSvgs = new Set<string>();
98
for (const file of readdirSync(new URL('../public/assets/img/svg', import.meta.url))) {
109
knownSvgs.add(parse(file).name);
1110
}
1211

1312
const rootPath = fileURLToPath(new URL('..', import.meta.url));
1413
let hadErrors = false;
1514

16-
for (const file of fastGlob.sync(fileURLToPath(new URL('../templates/**/*.tmpl', import.meta.url)))) {
15+
for (const file of globSync(fileURLToPath(new URL('../templates/**/*.tmpl', import.meta.url)))) {
1716
const content = readFileSync(file, 'utf8');
1817
for (const [_, name] of content.matchAll(/svg ["'`]([^"'`]+)["'`]/g)) {
1918
if (!knownSvgs.has(name)) {

0 commit comments

Comments
 (0)