mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 10:56:10 +01:00 
			
		
		
		
	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.
This commit is contained in:
		| @@ -18,6 +18,9 @@ RUN apk --no-cache add \ | |||||||
|     && npm install -g pnpm@10 \ |     && npm install -g pnpm@10 \ | ||||||
|     && rm -rf /var/cache/apk/* |     && 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 | # Setup repo | ||||||
| COPY . ${GOPATH}/src/code.gitea.io/gitea | COPY . ${GOPATH}/src/code.gitea.io/gitea | ||||||
| WORKDIR ${GOPATH}/src/code.gitea.io/gitea | WORKDIR ${GOPATH}/src/code.gitea.io/gitea | ||||||
|   | |||||||
| @@ -18,6 +18,9 @@ RUN apk --no-cache add \ | |||||||
|     && npm install -g pnpm@10 \ |     && npm install -g pnpm@10 \ | ||||||
|     && rm -rf /var/cache/apk/* |     && 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 | # Setup repo | ||||||
| COPY . ${GOPATH}/src/code.gitea.io/gitea | COPY . ${GOPATH}/src/code.gitea.io/gitea | ||||||
| WORKDIR ${GOPATH}/src/code.gitea.io/gitea | WORKDIR ${GOPATH}/src/code.gitea.io/gitea | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								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/...) | 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_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 := public/assets/js/index.js public/assets/css/index.css | ||||||
| WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts | 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 | GO_DIRS := build cmd models modules routers services tests | ||||||
| WEB_DIRS := web_src/js web_src/css | 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 | 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 | EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini | ||||||
|  |  | ||||||
| GO_SOURCES := $(wildcard *.go) | GO_SOURCES := $(wildcard *.go) | ||||||
| @@ -407,7 +407,7 @@ lint-actions: ## lint action workflow files | |||||||
|  |  | ||||||
| .PHONY: lint-templates | .PHONY: lint-templates | ||||||
| lint-templates: .venv node_modules ## lint template files | 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') | 	@uv run --frozen djlint $(shell find templates -type f -iname '*.tmpl') | ||||||
|  |  | ||||||
| .PHONY: lint-yaml | .PHONY: lint-yaml | ||||||
| @@ -421,7 +421,7 @@ watch: ## watch everything and continuously rebuild | |||||||
| .PHONY: watch-frontend | .PHONY: watch-frontend | ||||||
| watch-frontend: node-check node_modules ## watch frontend files and continuously rebuild | watch-frontend: node-check node_modules ## watch frontend files and continuously rebuild | ||||||
| 	@rm -rf $(WEBPACK_DEST_ENTRIES) | 	@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 | .PHONY: watch-backend | ||||||
| watch-backend: go-check ## watch backend files and continuously rebuild | 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 | 	@$(MAKE) -s node-check node_modules | ||||||
| 	@rm -rf $(WEBPACK_DEST_ENTRIES) | 	@rm -rf $(WEBPACK_DEST_ENTRIES) | ||||||
| 	@echo "Running webpack..." | 	@echo "Running webpack..." | ||||||
| 	@BROWSERSLIST_IGNORE_OLD_DATA=true pnpm exec webpack | 	@BROWSERSLIST_IGNORE_OLD_DATA=true pnpm exec webpack --disable-interpret | ||||||
| 	@touch $(WEBPACK_DEST) | 	@touch $(WEBPACK_DEST) | ||||||
|  |  | ||||||
| .PHONY: svg | .PHONY: svg | ||||||
| svg: node-check | node_modules ## build svg files | svg: node-check | node_modules ## build svg files | ||||||
| 	rm -rf $(SVG_DEST_DIR) | 	rm -rf $(SVG_DEST_DIR) | ||||||
| 	node tools/generate-svg.js | 	node tools/generate-svg.ts | ||||||
|  |  | ||||||
| .PHONY: svg-check | .PHONY: svg-check | ||||||
| svg-check: svg | svg-check: svg | ||||||
| @@ -922,7 +922,7 @@ generate-gitignore: ## update gitignore files | |||||||
|  |  | ||||||
| .PHONY: generate-images | .PHONY: generate-images | ||||||
| generate-images: | node_modules ## 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 | .PHONY: generate-manpage | ||||||
| generate-manpage: ## generate manpage | generate-manpage: ## generate manpage | ||||||
|   | |||||||
| @@ -2,8 +2,8 @@ | |||||||
|   "type": "module", |   "type": "module", | ||||||
|   "packageManager": "pnpm@10.0.0", |   "packageManager": "pnpm@10.0.0", | ||||||
|   "engines": { |   "engines": { | ||||||
|     "node": ">= 20.0.0", |     "node": ">= 22.18.0", | ||||||
|     "pnpm": ">=10.0.0" |     "pnpm": ">= 10.0.0" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@citation-js/core": "0.7.18", |     "@citation-js/core": "0.7.18", | ||||||
| @@ -31,7 +31,6 @@ | |||||||
|     "dropzone": "6.0.0-beta.2", |     "dropzone": "6.0.0-beta.2", | ||||||
|     "easymde": "2.20.0", |     "easymde": "2.20.0", | ||||||
|     "esbuild-loader": "4.3.0", |     "esbuild-loader": "4.3.0", | ||||||
|     "fast-glob": "3.3.3", |  | ||||||
|     "htmx.org": "2.0.6", |     "htmx.org": "2.0.6", | ||||||
|     "idiomorph": "0.7.3", |     "idiomorph": "0.7.3", | ||||||
|     "jquery": "3.7.1", |     "jquery": "3.7.1", | ||||||
| @@ -110,7 +109,6 @@ | |||||||
|     "stylelint-config-recommended": "17.0.0", |     "stylelint-config-recommended": "17.0.0", | ||||||
|     "stylelint-declaration-block-no-ignored-properties": "2.8.0", |     "stylelint-declaration-block-no-ignored-properties": "2.8.0", | ||||||
|     "stylelint-declaration-strict-value": "1.10.11", |     "stylelint-declaration-strict-value": "1.10.11", | ||||||
|     "stylelint-define-config": "16.22.0", |  | ||||||
|     "stylelint-value-no-unknown-custom-properties": "6.0.1", |     "stylelint-value-no-unknown-custom-properties": "6.0.1", | ||||||
|     "svgo": "4.0.0", |     "svgo": "4.0.0", | ||||||
|     "type-fest": "4.41.0", |     "type-fest": "4.41.0", | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										17
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @@ -83,9 +83,6 @@ importers: | |||||||
|       esbuild-loader: |       esbuild-loader: | ||||||
|         specifier: 4.3.0 |         specifier: 4.3.0 | ||||||
|         version: 4.3.0(webpack@5.101.0) |         version: 4.3.0(webpack@5.101.0) | ||||||
|       fast-glob: |  | ||||||
|         specifier: 3.3.3 |  | ||||||
|         version: 3.3.3 |  | ||||||
|       htmx.org: |       htmx.org: | ||||||
|         specifier: 2.0.6 |         specifier: 2.0.6 | ||||||
|         version: 2.0.6 |         version: 2.0.6 | ||||||
| @@ -315,9 +312,6 @@ importers: | |||||||
|       stylelint-declaration-strict-value: |       stylelint-declaration-strict-value: | ||||||
|         specifier: 1.10.11 |         specifier: 1.10.11 | ||||||
|         version: 1.10.11(stylelint@16.23.1(typescript@5.8.3)) |         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: |       stylelint-value-no-unknown-custom-properties: | ||||||
|         specifier: 6.0.1 |         specifier: 6.0.1 | ||||||
|         version: 6.0.1(stylelint@16.23.1(typescript@5.8.3)) |         version: 6.0.1(stylelint@16.23.1(typescript@5.8.3)) | ||||||
| @@ -4518,12 +4512,6 @@ packages: | |||||||
|     peerDependencies: |     peerDependencies: | ||||||
|       stylelint: '>=7 <=16' |       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: |   stylelint-value-no-unknown-custom-properties@6.0.1: | ||||||
|     resolution: {integrity: sha512-N60PTdaTknB35j6D4FhW0GL2LlBRV++bRpXMMldWMQZ240yFQaoltzlLY4lXXs7Z0J5mNUYZQ/gjyVtU2DhCMA==} |     resolution: {integrity: sha512-N60PTdaTknB35j6D4FhW0GL2LlBRV++bRpXMMldWMQZ240yFQaoltzlLY4lXXs7Z0J5mNUYZQ/gjyVtU2DhCMA==} | ||||||
|     engines: {node: '>=18.12.0'} |     engines: {node: '>=18.12.0'} | ||||||
| @@ -9732,11 +9720,6 @@ snapshots: | |||||||
|     dependencies: |     dependencies: | ||||||
|       stylelint: 16.23.1(typescript@5.8.3) |       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)): |   stylelint-value-no-unknown-custom-properties@6.0.1(stylelint@16.23.1(typescript@5.8.3)): | ||||||
|     dependencies: |     dependencies: | ||||||
|       postcss-value-parser: 4.2.0 |       postcss-value-parser: 4.2.0 | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| // @ts-check
 |  | ||||||
| import {defineConfig} from 'stylelint-define-config'; |  | ||||||
| import {fileURLToPath} from 'node:url'; | import {fileURLToPath} from 'node:url'; | ||||||
|  | import type {Config} from 'stylelint'; | ||||||
| 
 | 
 | ||||||
| const cssVarFiles = [ | const cssVarFiles = [ | ||||||
|   fileURLToPath(new URL('web_src/css/base.css', import.meta.url)), |   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)), |   fileURLToPath(new URL('web_src/css/themes/theme-gitea-dark.css', import.meta.url)), | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| export default defineConfig({ | export default { | ||||||
|   extends: 'stylelint-config-recommended', |   extends: 'stylelint-config-recommended', | ||||||
|   reportUnscopedDisables: true, |   reportUnscopedDisables: true, | ||||||
|   reportNeedlessDisables: true, |   reportNeedlessDisables: true, | ||||||
| @@ -124,7 +123,6 @@ export default defineConfig({ | |||||||
|     'csstools/value-no-unknown-custom-properties': [true, {importFrom: cssVarFiles}], |     'csstools/value-no-unknown-custom-properties': [true, {importFrom: cssVarFiles}], | ||||||
|     'declaration-block-no-duplicate-properties': [true, {ignore: ['consecutive-duplicates-with-different-values']}], |     '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']}], |     '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-unit-disallowed-list': {'line-height': ['em']}, | ||||||
|     'declaration-property-value-disallowed-list': {'word-break': ['break-word']}, |     'declaration-property-value-disallowed-list': {'word-break': ['break-word']}, | ||||||
|     'font-family-name-quotes': 'always-where-recommended', |     'font-family-name-quotes': 'always-where-recommended', | ||||||
| @@ -148,4 +146,4 @@ export default defineConfig({ | |||||||
|     'shorthand-property-no-redundant-values': true, |     'shorthand-property-no-redundant-values': true, | ||||||
|     'value-no-vendor-prefix': [true, {ignoreValues: ['box', 'inline-box']}], |     'value-no-vendor-prefix': [true, {ignoreValues: ['box', 'inline-box']}], | ||||||
|   }, |   }, | ||||||
| }); | } satisfies Config; | ||||||
| @@ -2,17 +2,18 @@ import {readFileSync} from 'node:fs'; | |||||||
| import {env} from 'node:process'; | import {env} from 'node:process'; | ||||||
| import {parse} from 'postcss'; | import {parse} from 'postcss'; | ||||||
| import plugin from 'tailwindcss/plugin.js'; | import plugin from 'tailwindcss/plugin.js'; | ||||||
|  | import type {Config} from 'tailwindcss'; | ||||||
| 
 | 
 | ||||||
| const isProduction = env.NODE_ENV !== 'development'; | const isProduction = env.NODE_ENV !== 'development'; | ||||||
| 
 | 
 | ||||||
| function extractRootVars(css) { | function extractRootVars(css: string) { | ||||||
|   const root = parse(css); |   const root = parse(css); | ||||||
|   const vars = new Set(); |   const vars = new Set<string>(); | ||||||
|   root.walkRules((rule) => { |   root.walkRules((rule) => { | ||||||
|     if (rule.selector !== ':root') return; |     if (rule.selector !== ':root') return; | ||||||
|     rule.each((decl) => { |     rule.each((node) => { | ||||||
|       if (decl.value && decl.prop.startsWith('--')) { |       if (node.type === 'decl' && node.value && node.prop.startsWith('--')) { | ||||||
|         vars.add(decl.prop.substring(2)); |         vars.add(node.prop.substring(2)); | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| @@ -120,4 +121,4 @@ export default { | |||||||
|       }); |       }); | ||||||
|     }), |     }), | ||||||
|   ], |   ], | ||||||
| }; | } satisfies Config; | ||||||
| @@ -4,12 +4,7 @@ import {optimize} from 'svgo'; | |||||||
| import {readFile, writeFile} from 'node:fs/promises'; | import {readFile, writeFile} from 'node:fs/promises'; | ||||||
| import {argv, exit} from 'node:process'; | import {argv, exit} from 'node:process'; | ||||||
| 
 | 
 | ||||||
| function doExit(err) { | async function generate(svg: string, path: string, {size, bg}: {size: number, bg?: boolean}) { | ||||||
|   if (err) console.error(err); |  | ||||||
|   exit(err ? 1 : 0); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| async function generate(svg, path, {size, bg}) { |  | ||||||
|   const outputFile = new URL(path, import.meta.url); |   const outputFile = new URL(path, import.meta.url); | ||||||
| 
 | 
 | ||||||
|   if (String(outputFile).endsWith('.svg')) { |   if (String(outputFile).endsWith('.svg')) { | ||||||
| @@ -19,7 +14,9 @@ async function generate(svg, path, {size, bg}) { | |||||||
|         'removeDimensions', |         'removeDimensions', | ||||||
|         { |         { | ||||||
|           name: 'addAttributesToSVGElement', |           name: 'addAttributesToSVGElement', | ||||||
|           params: {attributes: [{width: size}, {height: size}]}, |           params: { | ||||||
|  |             attributes: [{width: String(size)}, {height: String(size)}], | ||||||
|  |           }, | ||||||
|         }, |         }, | ||||||
|       ], |       ], | ||||||
|     }); |     }); | ||||||
| @@ -57,7 +54,8 @@ async function main() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| try { | try { | ||||||
|   doExit(await main()); |   await main(); | ||||||
| } catch (err) { | } catch (err) { | ||||||
|   doExit(err); |   console.error(err); | ||||||
|  |   exit(1); | ||||||
| } | } | ||||||
| @@ -1,27 +1,29 @@ | |||||||
| #!/usr/bin/env node | #!/usr/bin/env node | ||||||
| import fastGlob from 'fast-glob'; |  | ||||||
| import {optimize} from 'svgo'; | 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 {readFile, writeFile, mkdir} from 'node:fs/promises'; | ||||||
| import {fileURLToPath} from 'node:url'; | import {fileURLToPath} from 'node:url'; | ||||||
| import {exit} from 'node:process'; | import {exit} from 'node:process'; | ||||||
| import * as fs from 'node:fs'; | import type {Manifest} from 'material-icon-theme'; | ||||||
| 
 | 
 | ||||||
| const glob = (pattern) => fastGlob.sync(pattern, { | const glob = (pattern: string) => globSync(pattern, {cwd: dirname(import.meta.dirname)}); | ||||||
|   cwd: fileURLToPath(new URL('..', import.meta.url)), |  | ||||||
|   absolute: true, |  | ||||||
| }); |  | ||||||
| 
 | 
 | ||||||
| async function processAssetsSvgFile(file, {prefix, fullName} = {}) { | type Opts = { | ||||||
|  |   prefix?: string, | ||||||
|  |   fullName?: string, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | async function processAssetsSvgFile(path: string, {prefix, fullName}: Opts = {}) { | ||||||
|   let name = fullName; |   let name = fullName; | ||||||
|   if (!name) { |   if (!name) { | ||||||
|     name = parse(file).name; |     name = parse(path).name; | ||||||
|     if (prefix) name = `${prefix}-${name}`; |     if (prefix) name = `${prefix}-${name}`; | ||||||
|     if (prefix === 'octicon') name = name.replace(/-[0-9]+$/, ''); // chop of '-16' on octicons
 |     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
 |   // 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
 |   // 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: [ |     plugins: [ | ||||||
|       {name: 'preset-default'}, |       {name: 'preset-default'}, | ||||||
|       {name: 'removeDimensions'}, |       {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); |   await writeFile(fileURLToPath(new URL(`../public/assets/img/svg/${name}.svg`, import.meta.url)), data); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function processAssetsSvgFiles(pattern, opts) { | function processAssetsSvgFiles(pattern: string, opts: Opts = {}) { | ||||||
|   return glob(pattern).map((file) => processAssetsSvgFile(file, opts)); |   return glob(pattern).map((path) => processAssetsSvgFile(path, opts)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function processMaterialFileIcons() { | async function processMaterialFileIcons() { | ||||||
|   const files = glob('node_modules/material-icon-theme/icons/*.svg'); |   const paths = glob('node_modules/material-icon-theme/icons/*.svg'); | ||||||
|   const svgSymbols = {}; |   const svgSymbols: Record<string, string> = {}; | ||||||
|   for (const file of files) { |   for (const path of paths) { | ||||||
|     // remove all unnecessary attributes, only keep "viewBox"
 |     // remove all unnecessary attributes, only keep "viewBox"
 | ||||||
|     const {data} = optimize(await readFile(file, 'utf8'), { |     const {data} = optimize(await readFile(path, 'utf8'), { | ||||||
|       plugins: [ |       plugins: [ | ||||||
|         {name: 'preset-default'}, |         {name: 'preset-default'}, | ||||||
|         {name: 'removeDimensions'}, |         {name: 'removeDimensions'}, | ||||||
| @@ -58,16 +60,16 @@ async function processMaterialFileIcons() { | |||||||
|         {name: 'removeAttrs', params: {attrs: 'xml:space', elemSeparator: ','}}, |         {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
 |     // intentionally use single quote here to avoid escaping
 | ||||||
|     svgSymbols[svgName] = data.replace(/"/g, `'`); |     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 vscodeExtensionsJson = await readFile(fileURLToPath(new URL(`generate-svg-vscode-extensions.json`, import.meta.url)), 'utf8'); | ||||||
|   const vscodeExtensions = JSON.parse(vscodeExtensionsJson); |   const vscodeExtensions = JSON.parse(vscodeExtensionsJson) as Record<string, string>; | ||||||
|   const iconRulesJson = await readFile(fileURLToPath(new URL(`../node_modules/material-icon-theme/dist/material-icons.json`, import.meta.url))); |   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); |   const iconRules = JSON.parse(iconRulesJson) as Manifest; | ||||||
|   // The rules are from VSCode material-icon-theme, we need to adjust them to our needs
 |   // 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)
 |   // 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:
 |   // 2. We do not have a "Language ID" system:
 | ||||||
| @@ -91,7 +93,7 @@ async function processMaterialFileIcons() { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   const iconRulesPretty = JSON.stringify(iconRules, null, 2); |   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() { | async function main() { | ||||||
| @@ -1,11 +1,10 @@ | |||||||
| #!/usr/bin/env node | #!/usr/bin/env node | ||||||
| import {readdirSync, readFileSync} from 'node:fs'; | import {readdirSync, readFileSync, globSync} from 'node:fs'; | ||||||
| import {parse, relative} from 'node:path'; | import {parse, relative} from 'node:path'; | ||||||
| import {fileURLToPath} from 'node:url'; | import {fileURLToPath} from 'node:url'; | ||||||
| import {exit} from 'node:process'; | import {exit} from 'node:process'; | ||||||
| import fastGlob from 'fast-glob'; |  | ||||||
| 
 | 
 | ||||||
| const knownSvgs = new Set(); | const knownSvgs = new Set<string>(); | ||||||
| for (const file of readdirSync(new URL('../public/assets/img/svg', import.meta.url))) { | for (const file of readdirSync(new URL('../public/assets/img/svg', import.meta.url))) { | ||||||
|   knownSvgs.add(parse(file).name); |   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)); | const rootPath = fileURLToPath(new URL('..', import.meta.url)); | ||||||
| let hadErrors = false; | 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'); |   const content = readFileSync(file, 'utf8'); | ||||||
|   for (const [_, name] of content.matchAll(/svg ["'`]([^"'`]+)["'`]/g)) { |   for (const [_, name] of content.matchAll(/svg ["'`]([^"'`]+)["'`]/g)) { | ||||||
|     if (!knownSvgs.has(name)) { |     if (!knownSvgs.has(name)) { | ||||||
| @@ -41,6 +41,7 @@ | |||||||
|     "types": [ |     "types": [ | ||||||
|       "vitest/globals", |       "vitest/globals", | ||||||
|       "./web_src/js/globals.d.ts", |       "./web_src/js/globals.d.ts", | ||||||
|  |       "./types.d.ts", | ||||||
|     ], |     ], | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								types.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								types.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|  | } | ||||||
| @@ -1,3 +1,5 @@ | |||||||
|  | import type {Config} from 'updates'; | ||||||
|  | 
 | ||||||
| export default { | export default { | ||||||
|   exclude: [ |   exclude: [ | ||||||
|     '@mcaptcha/vanilla-glue', // breaking changes in rc versions need to be handled
 |     '@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
 |     'eslint-plugin-vitest', // need to migrate to eslint flat config first
 | ||||||
|     'tailwindcss', // need to migrate
 |     'tailwindcss', // need to migrate
 | ||||||
|   ], |   ], | ||||||
| }; | } satisfies Config; | ||||||
| @@ -1,4 +1,3 @@ | |||||||
| import fastGlob from 'fast-glob'; |  | ||||||
| import wrapAnsi from 'wrap-ansi'; | import wrapAnsi from 'wrap-ansi'; | ||||||
| import AddAssetPlugin from 'add-asset-webpack-plugin'; | import AddAssetPlugin from 'add-asset-webpack-plugin'; | ||||||
| import LicenseCheckerWebpackPlugin from '@techknowlogick/license-checker-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 MonacoWebpackPlugin from 'monaco-editor-webpack-plugin'; | ||||||
| import {VueLoaderPlugin} from 'vue-loader'; | import {VueLoaderPlugin} from 'vue-loader'; | ||||||
| import EsBuildLoader from 'esbuild-loader'; | import EsBuildLoader from 'esbuild-loader'; | ||||||
| import {parse, dirname} from 'node:path'; | import {parse} from 'node:path'; | ||||||
| import webpack from 'webpack'; | import webpack, {type Configuration, type EntryObject} from 'webpack'; | ||||||
| import {fileURLToPath} from 'node:url'; | import {fileURLToPath} from 'node:url'; | ||||||
| import {readFileSync} from 'node:fs'; | import {readFileSync, globSync} from 'node:fs'; | ||||||
| import {env} from 'node:process'; | import {env} from 'node:process'; | ||||||
| import tailwindcss from 'tailwindcss'; | import tailwindcss from 'tailwindcss'; | ||||||
| import tailwindConfig from './tailwind.config.js'; | import tailwindConfig from './tailwind.config.ts'; | ||||||
| import tailwindcssNesting from 'tailwindcss/nesting/index.js'; | import tailwindcssNesting from 'tailwindcss/nesting/index.js'; | ||||||
| import postcssNesting from 'postcss-nesting'; | import postcssNesting from 'postcss-nesting'; | ||||||
| 
 | 
 | ||||||
| const {EsbuildPlugin} = EsBuildLoader; | const {EsbuildPlugin} = EsBuildLoader; | ||||||
| const {SourceMapDevToolPlugin, DefinePlugin, EnvironmentPlugin} = webpack; | 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, { | const themes: EntryObject = {}; | ||||||
|   cwd: dirname(fileURLToPath(new URL(import.meta.url))), | for (const path of globSync('web_src/css/themes/*.css', {cwd: import.meta.dirname})) { | ||||||
|   absolute: true, |   themes[parse(path).name] = [`./${path}`]; | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| const themes = {}; |  | ||||||
| for (const path of glob('web_src/css/themes/*.css')) { |  | ||||||
|   themes[parse(path).name] = [path]; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const isProduction = env.NODE_ENV !== 'development'; | const isProduction = env.NODE_ENV !== 'development'; | ||||||
| @@ -55,12 +49,12 @@ const webComponents = new Set([ | |||||||
|   'text-expander', |   'text-expander', | ||||||
| ]); | ]); | ||||||
| 
 | 
 | ||||||
| const filterCssImport = (url, ...args) => { | const filterCssImport = (url: string, ...args: Array<any>) => { | ||||||
|   const cssFile = args[1] || args[0]; // resourcePath is 2nd argument for url and 3rd for import
 |   const cssFile = args[1] || args[0]; // resourcePath is 2nd argument for url and 3rd for import
 | ||||||
|   const importedFile = url.replace(/[?#].+/, '').toLowerCase(); |   const importedFile = url.replace(/[?#].+/, '').toLowerCase(); | ||||||
| 
 | 
 | ||||||
|   if (cssFile.includes('fomantic')) { |   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; |     if (/(eot|ttf|otf|woff|svg)$/i.test(importedFile)) return false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @@ -71,7 +65,6 @@ const filterCssImport = (url, ...args) => { | |||||||
|   return true; |   return true; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /** @type {import("webpack").Configuration} */ |  | ||||||
| export default { | export default { | ||||||
|   mode: isProduction ? 'production' : 'development', |   mode: isProduction ? 'production' : 'development', | ||||||
|   entry: { |   entry: { | ||||||
| @@ -100,7 +93,7 @@ export default { | |||||||
|     path: fileURLToPath(new URL('public/assets', import.meta.url)), |     path: fileURLToPath(new URL('public/assets', import.meta.url)), | ||||||
|     filename: () => 'js/[name].js', |     filename: () => 'js/[name].js', | ||||||
|     chunkFilename: ({chunk}) => { |     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`; |       return `js/${language ? `monaco-language-${language.toLowerCase()}` : `[name]`}.[contenthash:8].js`; | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
| @@ -129,7 +122,7 @@ export default { | |||||||
|         loader: 'vue-loader', |         loader: 'vue-loader', | ||||||
|         options: { |         options: { | ||||||
|           compilerOptions: { |           compilerOptions: { | ||||||
|             isCustomElement: (tag) => webComponents.has(tag), |             isCustomElement: (tag: string) => webComponents.has(tag), | ||||||
|           }, |           }, | ||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
| @@ -225,10 +218,10 @@ export default { | |||||||
|     }), |     }), | ||||||
|     isProduction ? new LicenseCheckerWebpackPlugin({ |     isProduction ? new LicenseCheckerWebpackPlugin({ | ||||||
|       outputFilename: 'licenses.txt', |       outputFilename: 'licenses.txt', | ||||||
|       outputWriter: ({dependencies}) => { |       outputWriter: ({dependencies}: {dependencies: Array<Record<string, string>>}) => { | ||||||
|         const line = '-'.repeat(80); |         const line = '-'.repeat(80); | ||||||
|         const goJson = readFileSync('assets/go-licenses.json', 'utf8'); |         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<string, string>) => { | ||||||
|           return {name, body: formatLicenseText(licenseText)}; |           return {name, body: formatLicenseText(licenseText)}; | ||||||
|         }); |         }); | ||||||
|         const jsModules = dependencies.map(({name, version, licenseName, licenseText}) => { |         const jsModules = dependencies.map(({name, version, licenseName, licenseText}) => { | ||||||
| @@ -285,4 +278,4 @@ export default { | |||||||
|     reasons: false, |     reasons: false, | ||||||
|     runtimeModules: false, |     runtimeModules: false, | ||||||
|   }, |   }, | ||||||
| }; | } satisfies Configuration; | ||||||
		Reference in New Issue
	
	Block a user