Skip to content

Commit d443f5e

Browse files
authored
refactor: vite and typescript related upgrades (vite 8 compatibility) (#2710)
1 parent a97f069 commit d443f5e

36 files changed

Lines changed: 4335 additions & 1378 deletions

package-lock.json

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

packages/alphatab/package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,14 @@
6363
"@coderline/alphaskia": "^3.5.147",
6464
"@coderline/alphaskia-linux": "^3.5.147",
6565
"@coderline/alphaskia-windows": "^3.5.147",
66-
"@types/node": "^25.2.2",
67-
"rimraf": "^6.1.2",
66+
"@types/node": "^25.9.1",
67+
"rimraf": "^6.1.3",
6868
"tslib": "^2.8.1",
69-
"tsx": "^4.21.0",
70-
"typescript": "^5.9.3",
71-
"vite": "^7.3.1",
72-
"vite-plugin-static-copy": "^3.2.0",
73-
"vitest": "^4.1.6"
69+
"tsx": "^4.22.3",
70+
"typescript": "^6.0.3",
71+
"vite": "^8.0.14",
72+
"vite-plugin-static-copy": "^4.1.0",
73+
"vitest": "^4.1.7"
7474
},
7575
"files": [
7676
"/dist/alphaTab*.js",

packages/alphatab/scripts/TypeSchema.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,9 +302,9 @@ export function getTypeWithNullableInfo(
302302
fillBaseInfoFrom(node);
303303
} else if (node.isUnion()) {
304304
for (const t of node.types) {
305-
if ((t.flags & ts.TypeFlags.Null) !== 0) {
305+
if (t === checker.getNullType()) {
306306
typeInfo.isNullable = true;
307-
} else if ((t.flags & ts.TypeFlags.Undefined) !== 0) {
307+
} else if (t === checker.getUndefinedType()) {
308308
typeInfo.isOptional = true;
309309
} else if (!mainType) {
310310
fillBaseInfoFrom(t);

packages/alphatab/src/alphaTab.main.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,10 @@ if (alphaTab.Environment.isRunningInWorker) {
114114
alphaTab.Environment.isViteBundled
115115
) {
116116
alphaTab.Logger.debug('AlphaTab', 'Creating Module worklet');
117-
const alphaTabWorklet = context.audioWorklet; // this name triggers the WebPack Plugin
117+
// destructure-rename keeps the `alphaTabWorklet` binding alive past
118+
// rolldown's single-use inlining and isolates the call from the
119+
// built-in bundler handlers that match on `audioWorklet.addModule`.
120+
const { audioWorklet: alphaTabWorklet } = context;
118121
return alphaTabWorklet.addModule(
119122
new alphaTab.Environment.alphaTabUrl('./alphaTab.worklet.ts', import.meta.url)
120123
);

packages/alphatab/vite.config.ts

Lines changed: 79 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import path from 'node:path';
22
import url from 'node:url';
3-
import type { RollupTypescriptOptions } from '@rollup/plugin-typescript';
3+
import type { Class, VariableDeclaration } from '@oxc-project/types';
44
import MagicString from 'magic-string';
5+
import { parseAst } from 'rolldown/parseAst';
56
import type { OutputOptions } from 'rollup';
67
import { defineConfig, type LibraryOptions, type Plugin } from 'vite';
78
import { viteStaticCopy } from 'vite-plugin-static-copy';
8-
import { defaultBuildUserConfig, umd, esm as defaultEsm, dtsPathsTransformer } from '../tooling/src/vite';
9-
import { elementStyleUsingTransformer } from '../tooling/src/typescript';
9+
import { addDts, defaultBuildUserConfig, esm, umd } from '../tooling/src/vite';
1010

1111
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
1212

@@ -30,59 +30,105 @@ const adjustScriptPathsPlugin = (min: boolean) => {
3030
} satisfies Plugin;
3131
};
3232

33+
// Rolldown unconditionally lowers top-level `class X { ... }` declarations to
34+
// `var X = class { ... };` (documented as TDZ-hoisting behaviour, independent
35+
// of `output.topLevelVar`). Downstream tooling that pattern-matches the
36+
// published bundle on `class X` substrings — like the alphatab webpack plugin
37+
// — depends on the declaration form, so we restore it here.
38+
const preserveClassDeclarationsPlugin = (): Plugin => ({
39+
name: 'preserve-class-declarations',
40+
renderChunk(code) {
41+
const program = parseAst(code, { lang: 'js', sourceType: 'module' });
42+
const ms = new MagicString(code);
43+
let changed = false;
44+
45+
for (const stmt of program.body) {
46+
if (stmt.type !== 'VariableDeclaration') {
47+
continue;
48+
}
49+
const varStmt = stmt as VariableDeclaration;
50+
if (varStmt.declarations.length !== 1) {
51+
continue;
52+
}
53+
const decl = varStmt.declarations[0];
54+
if (decl.id.type !== 'Identifier' || !decl.init || decl.init.type !== 'ClassExpression') {
55+
continue;
56+
}
57+
const classExpr = decl.init as Class;
58+
// skip when the inner name differs from the outer binding — that
59+
// would change semantics if the body self-references the inner name.
60+
if (classExpr.id && classExpr.id.name !== decl.id.name) {
61+
continue;
62+
}
63+
64+
// drop `var <id> = ` prefix, leaving the class expression
65+
ms.remove(varStmt.start, classExpr.start);
66+
// promote to class declaration: insert the binding name after `class`
67+
// if it isn't already present as an inner name.
68+
if (!classExpr.id) {
69+
ms.appendLeft(classExpr.start + 'class'.length, ` ${decl.id.name}`);
70+
}
71+
// drop the trailing semicolon of the var statement
72+
if (code[varStmt.end - 1] === ';') {
73+
ms.remove(varStmt.end - 1, varStmt.end);
74+
}
75+
changed = true;
76+
}
77+
78+
if (!changed) {
79+
return null;
80+
}
81+
return { code: ms.toString(), map: ms.generateMap({ hires: 'boundary' }) };
82+
}
83+
});
84+
3385
export default defineConfig(({ mode }) => {
34-
const config = defaultBuildUserConfig();
86+
const config = defaultBuildUserConfig(__dirname);
3587
config.plugins!.push(
3688
viteStaticCopy({
89+
// `stripBase` flattens so files land directly under `font/` and
90+
// `soundfont/` instead of preserving the `font/bravura/` prefix.
3791
targets: [
38-
{ src: 'font/bravura/*', dest: 'font/' },
39-
{ src: 'font/sonivox/*', dest: 'soundfont/' }
92+
{ src: 'font/bravura/*', dest: 'font/', rename: { stripBase: true } },
93+
{ src: 'font/sonivox/*', dest: 'soundfont/', rename: { stripBase: true } }
4094
]
4195
})
4296
);
4397

4498
const lib = config.build!.lib! as LibraryOptions;
4599
lib.name = 'alphaTab';
46100

47-
const typeScriptOptions = (): Partial<RollupTypescriptOptions> => {
48-
return {
49-
transformers: {
50-
before: [elementStyleUsingTransformer()],
51-
afterDeclarations: [dtsPathsTransformer()]
52-
}
53-
};
54-
};
55-
56-
const esm = (name: string, entry: string) => {
57-
defaultEsm(
58-
config,
59-
__dirname,
60-
name,
61-
entry,
62-
typeScriptOptions(),
63-
chunk => !chunk.facadeModuleId!.endsWith('alphaTab.core.ts')
64-
);
65-
66-
(config.build!.rollupOptions!.external as string[]).push('@coderline/alphatab/alphaTab.core');
67-
};
68-
69101
switch (mode) {
70102
case 'umd':
71-
umd(config, __dirname, 'alphaTab', 'src/alphaTab.main.ts', typeScriptOptions(), true);
103+
umd(config, __dirname, 'alphaTab', 'src/alphaTab.main.ts', true);
72104
break;
73105
//case 'esm':
74-
default:
75-
esm('alphaTab', 'src/alphaTab.main.ts');
76-
const entry = lib.entry! as Record<string, string>;
106+
default: {
107+
esm(config, __dirname, 'alphaTab', 'src/alphaTab.main.ts');
108+
109+
const entry = lib.entry as Record<string, string>;
77110
entry['alphaTab.core'] = path.resolve(__dirname, 'src/alphaTab.core.ts');
78111
entry['alphaTab.worker'] = path.resolve(__dirname, 'src/alphaTab.worker.ts');
79112
entry['alphaTab.worklet'] = path.resolve(__dirname, 'src/alphaTab.worklet.ts');
80113

114+
(config.build!.rollupOptions!.external as string[]).push('@coderline/alphatab/alphaTab.core');
115+
81116
for (const output of config.build!.rollupOptions!.output as OutputOptions[]) {
82117
const isMin = (output.entryFileNames as string).includes('.min');
83-
(output.plugins as Plugin[]).push(adjustScriptPathsPlugin(isMin));
118+
(output.plugins as Plugin[]).push(
119+
adjustScriptPathsPlugin(isMin),
120+
preserveClassDeclarationsPlugin()
121+
);
84122
}
123+
124+
// alphaTab.core is an internal runtime-split JS chunk; its types
125+
// are already re-exported through alphaTab.main, so no separate
126+
// bundled `alphaTab.core.d.ts` is published.
127+
addDts(config, __dirname, {
128+
shouldEmitForChunk: chunk => !chunk.facadeModuleId!.endsWith('alphaTab.core.ts')
129+
});
85130
break;
131+
}
86132
}
87133

88134
return config;

packages/alphatex/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"rimraf": "^6.1.3",
1414
"tslib": "^2.8.1",
1515
"tsx": "^4.21.0",
16-
"typescript": "^5.9.3",
16+
"typescript": "^6.0.3",
1717
"vitest": "^4.1.6"
1818
},
1919
"type": "module"

packages/csharp/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
},
1515
"devDependencies": {
1616
"@coderline/alphatab-transpiler": "*",
17-
"rimraf": "^6.1.3"
17+
"rimraf": "^6.1.3",
18+
"typescript": "^6.0.3"
1819
}
1920
}

packages/csharp/vite.config.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
import { defaultClientMainFields, defineConfig } from 'vite';
2-
import { defaultBuildUserConfig, dtsPathsTransformer, esm } from '../tooling/src/vite';
2+
import { addDts, defaultBuildUserConfig, esm } from '../tooling/src/vite';
33

44
export default defineConfig(() => {
5-
const config = defaultBuildUserConfig();
5+
const config = defaultBuildUserConfig(import.meta.dirname);
66
config.build!.sourcemap = true;
77
config.resolve ??= {};
88
config.resolve.mainFields = defaultClientMainFields.filter(f => f !== 'browser');
99

10-
esm(config, import.meta.dirname, 'server', 'src/index.ts', {
11-
module: 'preserve',
12-
transformers: {
13-
afterDeclarations: [
14-
dtsPathsTransformer()
15-
]
16-
}
17-
});
10+
esm(config, import.meta.dirname, 'server', 'src/index.ts');
1811
(config.build!.rollupOptions!.external as (RegExp | string)[]).push('@coderline/alphatab');
12+
addDts(config, import.meta.dirname);
1913

2014
return config;
2115
});

packages/kotlin/vite.config.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
import { defaultClientMainFields, defineConfig } from 'vite';
2-
import { defaultBuildUserConfig, dtsPathsTransformer, esm } from '../tooling/src/vite';
2+
import { addDts, defaultBuildUserConfig, esm } from '../tooling/src/vite';
33

44
export default defineConfig(() => {
5-
const config = defaultBuildUserConfig();
5+
const config = defaultBuildUserConfig(import.meta.dirname);
66
config.build!.sourcemap = true;
77
config.resolve ??= {};
88
config.resolve.mainFields = defaultClientMainFields.filter(f => f !== 'browser');
99

10-
esm(config, import.meta.dirname, 'server', 'src/index.ts', {
11-
module: 'preserve',
12-
transformers: {
13-
afterDeclarations: [
14-
dtsPathsTransformer()
15-
]
16-
}
17-
});
10+
esm(config, import.meta.dirname, 'server', 'src/index.ts');
1811
(config.build!.rollupOptions!.external as (RegExp | string)[]).push('@coderline/alphatab');
12+
addDts(config, import.meta.dirname);
1913

2014
return config;
2115
});

packages/lsp/package.json

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,12 @@
4242
"devDependencies": {
4343
"@biomejs/biome": "^2.4.15",
4444
"@microsoft/api-extractor": "^7.57.7",
45-
"@types/node": "^25.6.0",
45+
"@types/node": "^25.9.1",
4646
"rimraf": "^6.1.3",
4747
"tslib": "^2.8.1",
48-
"tsx": "^4.21.0",
49-
"typescript": "^5.9.3",
50-
"vite-tsconfig-paths": "^6.1.1",
51-
"vitest": "^4.1.6"
48+
"tsx": "^4.22.3",
49+
"typescript": "^6.0.3",
50+
"vitest": "^4.1.7"
5251
},
5352
"bin": "./dist/server.mjs",
5453
"main": "./dist/server.mjs",

0 commit comments

Comments
 (0)