redscript-mc 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.yml +72 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +57 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +17 -25
- package/CHANGELOG.md +112 -0
- package/CONTRIBUTING.md +140 -0
- package/README.md +28 -19
- package/README.zh.md +28 -19
- package/dist/__tests__/cli.test.js +148 -10
- package/dist/__tests__/codegen.test.js +26 -1
- package/dist/__tests__/diagnostics.test.js +5 -5
- package/dist/__tests__/e2e.test.js +336 -17
- package/dist/__tests__/formatter.test.d.ts +1 -0
- package/dist/__tests__/formatter.test.js +40 -0
- package/dist/__tests__/lexer.test.js +12 -2
- package/dist/__tests__/lowering.test.js +200 -12
- package/dist/__tests__/mc-integration.test.js +370 -31
- package/dist/__tests__/mc-syntax.test.js +3 -3
- package/dist/__tests__/nbt.test.js +2 -2
- package/dist/__tests__/optimizer-advanced.test.js +5 -5
- package/dist/__tests__/parser.test.js +80 -0
- package/dist/__tests__/runtime.test.js +9 -9
- package/dist/__tests__/typechecker.test.js +158 -0
- package/dist/ast/types.d.ts +40 -3
- package/dist/cli.js +25 -7
- package/dist/codegen/mcfunction/index.d.ts +1 -1
- package/dist/codegen/mcfunction/index.js +38 -3
- package/dist/codegen/structure/index.js +32 -1
- package/dist/compile.d.ts +10 -0
- package/dist/compile.js +36 -5
- package/dist/events/types.d.ts +35 -0
- package/dist/events/types.js +59 -0
- package/dist/formatter/index.d.ts +1 -0
- package/dist/formatter/index.js +26 -0
- package/dist/index.js +3 -2
- package/dist/ir/builder.d.ts +2 -1
- package/dist/ir/types.d.ts +11 -2
- package/dist/ir/types.js +1 -1
- package/dist/lexer/index.d.ts +1 -1
- package/dist/lexer/index.js +2 -0
- package/dist/lowering/index.d.ts +34 -1
- package/dist/lowering/index.js +622 -23
- package/dist/mc-test/runner.d.ts +2 -2
- package/dist/mc-test/runner.js +3 -3
- package/dist/mc-test/setup.js +2 -2
- package/dist/parser/index.d.ts +4 -0
- package/dist/parser/index.js +153 -16
- package/dist/typechecker/index.d.ts +17 -0
- package/dist/typechecker/index.js +343 -17
- package/docs/COMPILATION_STATS.md +24 -24
- package/docs/ENTITY_TYPE_SYSTEM.md +242 -0
- package/docs/IMPLEMENTATION_GUIDE.md +1 -1
- package/docs/STRUCTURE_TARGET.md +1 -1
- package/editors/vscode/.vscodeignore +1 -0
- package/editors/vscode/CHANGELOG.md +9 -0
- package/editors/vscode/icons/mcrs.svg +7 -0
- package/editors/vscode/icons/redscript-icons.json +10 -0
- package/editors/vscode/out/extension.js +1295 -80
- package/editors/vscode/package-lock.json +2 -2
- package/editors/vscode/package.json +10 -3
- package/editors/vscode/src/hover.ts +55 -2
- package/editors/vscode/src/symbols.ts +42 -0
- package/package.json +1 -1
- package/src/__tests__/cli.test.ts +176 -10
- package/src/__tests__/codegen.test.ts +28 -1
- package/src/__tests__/diagnostics.test.ts +5 -5
- package/src/__tests__/e2e.test.ts +335 -17
- package/src/__tests__/fixtures/event-test.mcrs +13 -0
- package/src/__tests__/fixtures/impl-test.mcrs +46 -0
- package/src/__tests__/fixtures/interval-test.mcrs +11 -0
- package/src/__tests__/fixtures/is-check-test.mcrs +20 -0
- package/src/__tests__/fixtures/timeout-test.mcrs +7 -0
- package/src/__tests__/lexer.test.ts +14 -2
- package/src/__tests__/lowering.test.ts +226 -12
- package/src/__tests__/mc-integration.test.ts +421 -31
- package/src/__tests__/mc-syntax.test.ts +3 -3
- package/src/__tests__/nbt.test.ts +2 -2
- package/src/__tests__/optimizer-advanced.test.ts +5 -5
- package/src/__tests__/parser.test.ts +91 -5
- package/src/__tests__/runtime.test.ts +9 -9
- package/src/__tests__/typechecker.test.ts +171 -0
- package/src/ast/types.ts +44 -3
- package/src/cli.ts +10 -10
- package/src/codegen/mcfunction/index.ts +40 -3
- package/src/codegen/structure/index.ts +35 -1
- package/src/compile.ts +54 -6
- package/src/events/types.ts +69 -0
- package/src/examples/capture_the_flag.mcrs +208 -0
- package/src/examples/{counter.rs → counter.mcrs} +1 -1
- package/src/examples/hunger_games.mcrs +301 -0
- package/src/examples/new_features_demo.mcrs +193 -0
- package/src/examples/parkour_race.mcrs +233 -0
- package/src/examples/rpg.mcrs +13 -0
- package/src/examples/{shop.rs → shop.mcrs} +1 -1
- package/src/examples/{showcase_game.rs → showcase_game.mcrs} +3 -3
- package/src/examples/{turret.rs → turret.mcrs} +1 -1
- package/src/examples/zombie_survival.mcrs +314 -0
- package/src/index.ts +4 -3
- package/src/ir/builder.ts +3 -1
- package/src/ir/types.ts +12 -2
- package/src/lexer/index.ts +3 -1
- package/src/lowering/index.ts +684 -24
- package/src/mc-test/runner.ts +3 -3
- package/src/mc-test/setup.ts +2 -2
- package/src/parser/index.ts +170 -19
- package/src/stdlib/README.md +178 -140
- package/src/stdlib/bossbar.mcrs +68 -0
- package/src/stdlib/{cooldown.rs → cooldown.mcrs} +1 -1
- package/src/stdlib/effects.mcrs +64 -0
- package/src/stdlib/interactions.mcrs +195 -0
- package/src/stdlib/inventory.mcrs +38 -0
- package/src/stdlib/mobs.mcrs +99 -0
- package/src/stdlib/particles.mcrs +52 -0
- package/src/stdlib/sets.mcrs +20 -0
- package/src/stdlib/spawn.mcrs +41 -0
- package/src/stdlib/tags.mcrs +951 -0
- package/src/stdlib/teams.mcrs +68 -0
- package/src/stdlib/timer.mcrs +72 -0
- package/src/stdlib/world.mcrs +92 -0
- package/src/typechecker/index.ts +404 -18
- package/src/examples/rpg.rs +0 -13
- package/src/stdlib/mobs.rs +0 -99
- package/src/stdlib/timer.rs +0 -51
- /package/src/examples/{arena.rs → arena.mcrs} +0 -0
- /package/src/examples/{pvp_arena.rs → pvp_arena.mcrs} +0 -0
- /package/src/examples/{quiz.rs → quiz.mcrs} +0 -0
- /package/src/examples/{stdlib_demo.rs → stdlib_demo.mcrs} +0 -0
- /package/src/examples/{world_manager.rs → world_manager.mcrs} +0 -0
- /package/src/stdlib/{combat.rs → combat.mcrs} +0 -0
- /package/src/stdlib/{math.rs → math.mcrs} +0 -0
- /package/src/stdlib/{player.rs → player.mcrs} +0 -0
- /package/src/stdlib/{strings.rs → strings.mcrs} +0 -0
- /package/src/templates/{combat.rs → combat.mcrs} +0 -0
- /package/src/templates/{economy.rs → economy.mcrs} +0 -0
- /package/src/templates/{mini-game-framework.rs → mini-game-framework.mcrs} +0 -0
- /package/src/templates/{quest.rs → quest.mcrs} +0 -0
- /package/src/test_programs/{zombie_game.rs → zombie_game.mcrs} +0 -0
|
@@ -43,10 +43,10 @@ describe('CLI API', () => {
|
|
|
43
43
|
describe('imports', () => {
|
|
44
44
|
it('compiles a file with imported helpers', () => {
|
|
45
45
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-imports-'));
|
|
46
|
-
const libPath = path.join(tempDir, 'lib.
|
|
47
|
-
const mainPath = path.join(tempDir, 'main.
|
|
46
|
+
const libPath = path.join(tempDir, 'lib.mcrs');
|
|
47
|
+
const mainPath = path.join(tempDir, 'main.mcrs');
|
|
48
48
|
fs.writeFileSync(libPath, 'fn double(x: int) -> int { return x + x; }\n');
|
|
49
|
-
fs.writeFileSync(mainPath, 'import "./lib.
|
|
49
|
+
fs.writeFileSync(mainPath, 'import "./lib.mcrs"\n\nfn main() { let value: int = double(2); }\n');
|
|
50
50
|
const source = fs.readFileSync(mainPath, 'utf-8');
|
|
51
51
|
const result = (0, index_1.compile)(source, { namespace: 'imports', filePath: mainPath });
|
|
52
52
|
expect(result.files.length).toBeGreaterThan(0);
|
|
@@ -55,17 +55,155 @@ describe('CLI API', () => {
|
|
|
55
55
|
});
|
|
56
56
|
it('deduplicates circular imports', () => {
|
|
57
57
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-circular-'));
|
|
58
|
-
const aPath = path.join(tempDir, 'a.
|
|
59
|
-
const bPath = path.join(tempDir, 'b.
|
|
60
|
-
const mainPath = path.join(tempDir, 'main.
|
|
61
|
-
fs.writeFileSync(aPath, 'import "./b.
|
|
62
|
-
fs.writeFileSync(bPath, 'import "./a.
|
|
63
|
-
fs.writeFileSync(mainPath, 'import "./a.
|
|
58
|
+
const aPath = path.join(tempDir, 'a.mcrs');
|
|
59
|
+
const bPath = path.join(tempDir, 'b.mcrs');
|
|
60
|
+
const mainPath = path.join(tempDir, 'main.mcrs');
|
|
61
|
+
fs.writeFileSync(aPath, 'import "./b.mcrs"\n\nfn from_a() -> int { return 1; }\n');
|
|
62
|
+
fs.writeFileSync(bPath, 'import "./a.mcrs"\n\nfn from_b() -> int { return from_a(); }\n');
|
|
63
|
+
fs.writeFileSync(mainPath, 'import "./a.mcrs"\n\nfn main() { let value: int = from_b(); }\n');
|
|
64
64
|
const source = fs.readFileSync(mainPath, 'utf-8');
|
|
65
65
|
const result = (0, index_1.compile)(source, { namespace: 'circular', filePath: mainPath });
|
|
66
66
|
expect(result.ir.functions.filter(fn => fn.name === 'from_a')).toHaveLength(1);
|
|
67
67
|
expect(result.ir.functions.filter(fn => fn.name === 'from_b')).toHaveLength(1);
|
|
68
68
|
});
|
|
69
|
+
it('uses rs-prefixed scoreboard objectives for imported stdlib files', () => {
|
|
70
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-stdlib-'));
|
|
71
|
+
const stdlibDir = path.join(tempDir, 'src', 'stdlib');
|
|
72
|
+
const stdlibPath = path.join(stdlibDir, 'timer.mcrs');
|
|
73
|
+
const mainPath = path.join(tempDir, 'main.mcrs');
|
|
74
|
+
fs.mkdirSync(stdlibDir, { recursive: true });
|
|
75
|
+
fs.writeFileSync(stdlibPath, 'fn tick_timer() { scoreboard_set("#rs", "timer_ticks", 1); }\n');
|
|
76
|
+
fs.writeFileSync(mainPath, 'import "./src/stdlib/timer.mcrs"\n\nfn main() { tick_timer(); }\n');
|
|
77
|
+
const source = fs.readFileSync(mainPath, 'utf-8');
|
|
78
|
+
const result = (0, index_1.compile)(source, { namespace: 'mygame', filePath: mainPath });
|
|
79
|
+
const tickTimer = result.files.find(file => file.path.endsWith('/tick_timer.mcfunction'));
|
|
80
|
+
expect(tickTimer?.content).toContain('scoreboard players set #rs rs.timer_ticks 1');
|
|
81
|
+
});
|
|
82
|
+
it('adds a call-site hash for stdlib internal scoreboard objectives', () => {
|
|
83
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-stdlib-hash-'));
|
|
84
|
+
const stdlibDir = path.join(tempDir, 'src', 'stdlib');
|
|
85
|
+
const stdlibPath = path.join(stdlibDir, 'timer.mcrs');
|
|
86
|
+
const mainPath = path.join(tempDir, 'main.mcrs');
|
|
87
|
+
fs.mkdirSync(stdlibDir, { recursive: true });
|
|
88
|
+
fs.writeFileSync(stdlibPath, [
|
|
89
|
+
'fn timer_start(name: string, duration: int) {',
|
|
90
|
+
' scoreboard_set("timer_ticks", #rs, duration);',
|
|
91
|
+
' scoreboard_set("timer_active", #rs, 1);',
|
|
92
|
+
'}',
|
|
93
|
+
'',
|
|
94
|
+
].join('\n'));
|
|
95
|
+
fs.writeFileSync(mainPath, [
|
|
96
|
+
'import "./src/stdlib/timer.mcrs"',
|
|
97
|
+
'',
|
|
98
|
+
'fn main() {',
|
|
99
|
+
' timer_start("x", 100);',
|
|
100
|
+
' timer_start("x", 100);',
|
|
101
|
+
'}',
|
|
102
|
+
'',
|
|
103
|
+
].join('\n'));
|
|
104
|
+
const source = fs.readFileSync(mainPath, 'utf-8');
|
|
105
|
+
const result = (0, index_1.compile)(source, { namespace: 'mygame', filePath: mainPath });
|
|
106
|
+
const timerFns = result.files.filter(file => /timer_start__callsite_[0-9a-f]{4}\.mcfunction$/.test(file.path));
|
|
107
|
+
expect(timerFns).toHaveLength(2);
|
|
108
|
+
const objectives = timerFns
|
|
109
|
+
.flatMap(file => [...file.content.matchAll(/rs\._timer_([0-9a-f]{4})/g)].map(match => match[0]));
|
|
110
|
+
expect(new Set(objectives).size).toBe(2);
|
|
111
|
+
});
|
|
112
|
+
it('Timer::new creates timer', () => {
|
|
113
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-timer-new-'));
|
|
114
|
+
const mainPath = path.join(tempDir, 'main.mcrs');
|
|
115
|
+
const timerPath = path.resolve(process.cwd(), 'src/stdlib/timer.mcrs');
|
|
116
|
+
fs.writeFileSync(mainPath, [
|
|
117
|
+
`import "${timerPath}"`,
|
|
118
|
+
'',
|
|
119
|
+
'fn main() {',
|
|
120
|
+
' let timer: Timer = Timer::new(20);',
|
|
121
|
+
'}',
|
|
122
|
+
'',
|
|
123
|
+
].join('\n'));
|
|
124
|
+
const source = fs.readFileSync(mainPath, 'utf-8');
|
|
125
|
+
const result = (0, index_1.compile)(source, { namespace: 'timernew', filePath: mainPath });
|
|
126
|
+
expect(result.typeErrors).toEqual([]);
|
|
127
|
+
const newFn = result.files.find(file => file.path.endsWith('/Timer_new.mcfunction'));
|
|
128
|
+
expect(newFn?.content).toContain('scoreboard players set timer_ticks rs 0');
|
|
129
|
+
expect(newFn?.content).toContain('scoreboard players set timer_active rs 0');
|
|
130
|
+
});
|
|
131
|
+
it('Timer.start/pause/reset', () => {
|
|
132
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-timer-state-'));
|
|
133
|
+
const mainPath = path.join(tempDir, 'main.mcrs');
|
|
134
|
+
const timerPath = path.resolve(process.cwd(), 'src/stdlib/timer.mcrs');
|
|
135
|
+
fs.writeFileSync(mainPath, [
|
|
136
|
+
`import "${timerPath}"`,
|
|
137
|
+
'',
|
|
138
|
+
'fn main() {',
|
|
139
|
+
' let timer: Timer = Timer::new(20);',
|
|
140
|
+
' timer.start();',
|
|
141
|
+
' timer.pause();',
|
|
142
|
+
' timer.reset();',
|
|
143
|
+
'}',
|
|
144
|
+
'',
|
|
145
|
+
].join('\n'));
|
|
146
|
+
const source = fs.readFileSync(mainPath, 'utf-8');
|
|
147
|
+
const result = (0, index_1.compile)(source, { namespace: 'timerstate', filePath: mainPath });
|
|
148
|
+
expect(result.typeErrors).toEqual([]);
|
|
149
|
+
const startFn = result.files.find(file => file.path.endsWith('/Timer_start.mcfunction'));
|
|
150
|
+
const pauseFn = result.files.find(file => file.path.endsWith('/Timer_pause.mcfunction'));
|
|
151
|
+
const resetFn = result.files.find(file => file.path.endsWith('/Timer_reset.mcfunction'));
|
|
152
|
+
expect(startFn?.content).toContain('scoreboard players set timer_active rs 1');
|
|
153
|
+
expect(pauseFn?.content).toContain('scoreboard players set timer_active rs 0');
|
|
154
|
+
expect(resetFn?.content).toContain('scoreboard players set timer_ticks rs 0');
|
|
155
|
+
});
|
|
156
|
+
it('Timer.done returns bool', () => {
|
|
157
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-timer-done-'));
|
|
158
|
+
const mainPath = path.join(tempDir, 'main.mcrs');
|
|
159
|
+
const timerPath = path.resolve(process.cwd(), 'src/stdlib/timer.mcrs');
|
|
160
|
+
fs.writeFileSync(mainPath, [
|
|
161
|
+
`import "${timerPath}"`,
|
|
162
|
+
'',
|
|
163
|
+
'fn main() {',
|
|
164
|
+
' let timer: Timer = Timer::new(20);',
|
|
165
|
+
' let finished: bool = timer.done();',
|
|
166
|
+
' if (finished) {',
|
|
167
|
+
' say("done");',
|
|
168
|
+
' }',
|
|
169
|
+
'}',
|
|
170
|
+
'',
|
|
171
|
+
].join('\n'));
|
|
172
|
+
const source = fs.readFileSync(mainPath, 'utf-8');
|
|
173
|
+
const result = (0, index_1.compile)(source, { namespace: 'timerdone', filePath: mainPath });
|
|
174
|
+
expect(result.typeErrors).toEqual([]);
|
|
175
|
+
const doneFn = result.files.find(file => file.path.endsWith('/Timer_done.mcfunction'));
|
|
176
|
+
const mainFn = result.files.find(file => file.path.endsWith('/main.mcfunction'));
|
|
177
|
+
expect(doneFn?.content).toContain('scoreboard players get timer_ticks rs');
|
|
178
|
+
expect(doneFn?.content).toContain('return run scoreboard players get');
|
|
179
|
+
expect(mainFn?.content).toContain('execute if score $finished rs matches 1..');
|
|
180
|
+
});
|
|
181
|
+
it('Timer.tick increments', () => {
|
|
182
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-timer-tick-'));
|
|
183
|
+
const mainPath = path.join(tempDir, 'main.mcrs');
|
|
184
|
+
const timerPath = path.resolve(process.cwd(), 'src/stdlib/timer.mcrs');
|
|
185
|
+
fs.writeFileSync(mainPath, [
|
|
186
|
+
`import "${timerPath}"`,
|
|
187
|
+
'',
|
|
188
|
+
'fn main() {',
|
|
189
|
+
' let timer: Timer = Timer::new(20);',
|
|
190
|
+
' timer.start();',
|
|
191
|
+
' timer.tick();',
|
|
192
|
+
'}',
|
|
193
|
+
'',
|
|
194
|
+
].join('\n'));
|
|
195
|
+
const source = fs.readFileSync(mainPath, 'utf-8');
|
|
196
|
+
const result = (0, index_1.compile)(source, { namespace: 'timertick', filePath: mainPath });
|
|
197
|
+
expect(result.typeErrors).toEqual([]);
|
|
198
|
+
const tickOutput = result.files
|
|
199
|
+
.filter(file => file.path.includes('/Timer_tick'))
|
|
200
|
+
.map(file => file.content)
|
|
201
|
+
.join('\n');
|
|
202
|
+
expect(tickOutput).toContain('scoreboard players get timer_active rs');
|
|
203
|
+
expect(tickOutput).toContain('scoreboard players get timer_ticks rs');
|
|
204
|
+
expect(tickOutput).toContain(' += $const_1 rs');
|
|
205
|
+
expect(tickOutput).toContain('execute store result score timer_ticks rs run scoreboard players get $_');
|
|
206
|
+
});
|
|
69
207
|
});
|
|
70
208
|
describe('compile()', () => {
|
|
71
209
|
it('compiles simple source', () => {
|
|
@@ -110,7 +248,7 @@ fn build() {
|
|
|
110
248
|
describe('--stats flag', () => {
|
|
111
249
|
it('prints optimizer statistics', () => {
|
|
112
250
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'redscript-stats-'));
|
|
113
|
-
const inputPath = path.join(tempDir, 'input.
|
|
251
|
+
const inputPath = path.join(tempDir, 'input.mcrs');
|
|
114
252
|
const outputDir = path.join(tempDir, 'out');
|
|
115
253
|
fs.writeFileSync(inputPath, 'fn build() { setblock((0, 64, 0), "minecraft:stone"); setblock((1, 64, 0), "minecraft:stone"); }');
|
|
116
254
|
const stdout = (0, child_process_1.execFileSync)(process.execPath, ['-r', 'ts-node/register', path.join(process.cwd(), 'src/cli.ts'), 'compile', inputPath, '-o', outputDir, '--stats'], { cwd: process.cwd(), encoding: 'utf-8' });
|
|
@@ -10,7 +10,7 @@ describe('generateDatapack', () => {
|
|
|
10
10
|
expect(JSON.parse(meta.content).pack.pack_format).toBe(26);
|
|
11
11
|
});
|
|
12
12
|
it('generates __load.mcfunction with objective setup', () => {
|
|
13
|
-
const mod = { namespace: 'mypack', functions: [], globals: ['counter'] };
|
|
13
|
+
const mod = { namespace: 'mypack', functions: [], globals: [{ name: 'counter', init: 0 }] };
|
|
14
14
|
const files = (0, mcfunction_1.generateDatapack)(mod);
|
|
15
15
|
const load = files.find(f => f.path.includes('__load.mcfunction'));
|
|
16
16
|
expect(load?.content).toContain('scoreboard objectives add rs dummy');
|
|
@@ -117,5 +117,30 @@ describe('generateDatapack', () => {
|
|
|
117
117
|
expect(json.criteria.trigger.trigger).toBe('minecraft:story/mine_diamond');
|
|
118
118
|
expect(json.rewards.function).toBe('mypack:on_mine_diamond');
|
|
119
119
|
});
|
|
120
|
+
it('generates static event dispatcher in __tick', () => {
|
|
121
|
+
const mod = {
|
|
122
|
+
namespace: 'mypack',
|
|
123
|
+
globals: [],
|
|
124
|
+
functions: [{
|
|
125
|
+
name: 'handle_death',
|
|
126
|
+
params: [],
|
|
127
|
+
locals: [],
|
|
128
|
+
blocks: [{ label: 'entry', instrs: [], term: { op: 'return' } }],
|
|
129
|
+
eventHandler: { eventType: 'PlayerDeath', tag: 'rs.just_died' },
|
|
130
|
+
}, {
|
|
131
|
+
name: 'handle_death_2',
|
|
132
|
+
params: [],
|
|
133
|
+
locals: [],
|
|
134
|
+
blocks: [{ label: 'entry', instrs: [], term: { op: 'return' } }],
|
|
135
|
+
eventHandler: { eventType: 'PlayerDeath', tag: 'rs.just_died' },
|
|
136
|
+
}],
|
|
137
|
+
};
|
|
138
|
+
const files = (0, mcfunction_1.generateDatapack)(mod);
|
|
139
|
+
const tickFn = files.find(f => f.path.includes('__tick.mcfunction'));
|
|
140
|
+
expect(tickFn).toBeDefined();
|
|
141
|
+
expect(tickFn.content).toContain('execute as @a[tag=rs.just_died] run function mypack:handle_death');
|
|
142
|
+
expect(tickFn.content).toContain('execute as @a[tag=rs.just_died] run function mypack:handle_death_2');
|
|
143
|
+
expect(tickFn.content).toContain('tag @a[tag=rs.just_died] remove rs.just_died');
|
|
144
|
+
});
|
|
120
145
|
});
|
|
121
146
|
//# sourceMappingURL=codegen.test.js.map
|
|
@@ -23,8 +23,8 @@ describe('DiagnosticError', () => {
|
|
|
23
23
|
});
|
|
24
24
|
it('includes file path when available', () => {
|
|
25
25
|
const source = 'let x = foo();';
|
|
26
|
-
const error = new diagnostics_1.DiagnosticError('TypeError', 'Unknown function: foo', { file: 'test.
|
|
27
|
-
expect((0, diagnostics_1.formatError)(error, source)).toContain('Error in test.
|
|
26
|
+
const error = new diagnostics_1.DiagnosticError('TypeError', 'Unknown function: foo', { file: 'test.mcrs', line: 1, col: 9 }, source.split('\n'));
|
|
27
|
+
expect((0, diagnostics_1.formatError)(error, source)).toContain('Error in test.mcrs at line 1, col 9:');
|
|
28
28
|
});
|
|
29
29
|
});
|
|
30
30
|
describe('format', () => {
|
|
@@ -43,9 +43,9 @@ describe('DiagnosticError', () => {
|
|
|
43
43
|
expect(formatted).toContain('^');
|
|
44
44
|
});
|
|
45
45
|
it('formats error with file path', () => {
|
|
46
|
-
const error = new diagnostics_1.DiagnosticError('LexError', 'Unexpected character', { file: 'test.
|
|
46
|
+
const error = new diagnostics_1.DiagnosticError('LexError', 'Unexpected character', { file: 'test.mcrs', line: 1, col: 1 }, ['@@@']);
|
|
47
47
|
const formatted = error.format();
|
|
48
|
-
expect(formatted).toContain('test.
|
|
48
|
+
expect(formatted).toContain('test.mcrs:');
|
|
49
49
|
expect(formatted).toContain('[LexError]');
|
|
50
50
|
});
|
|
51
51
|
it('handles missing source lines gracefully', () => {
|
|
@@ -110,7 +110,7 @@ describe('compile function', () => {
|
|
|
110
110
|
expect(result.error?.message).toContain("Expected ';'");
|
|
111
111
|
});
|
|
112
112
|
it('includes file path in error', () => {
|
|
113
|
-
const result = (0, compile_1.compile)('fn main() { }', { filePath: 'test.
|
|
113
|
+
const result = (0, compile_1.compile)('fn main() { }', { filePath: 'test.mcrs' });
|
|
114
114
|
// This is valid, but test that filePath is passed through
|
|
115
115
|
expect(result.success).toBe(true);
|
|
116
116
|
});
|