Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
122 commits
Select commit Hold shift + click to select a range
a8ce317
Add call action annotation overrides
Pollux12 May 31, 2026
6b46a60
Use generic call argument
Pollux12 Jun 7, 2026
6e30871
Annotate system call_arg
Pollux12 Jun 7, 2026
30cf125
Annotate VGUI call_arg
Pollux12 Jun 7, 2026
bf9cadf
Annotate Derma control and hook call_arg
Pollux12 Jun 8, 2026
9bb6047
Annotation NetworkVarElement call_arg
Pollux12 Jun 8, 2026
d3f14bc
Stop zip stream after missing input
Pollux12 Jun 8, 2026
ed382e7
Annotate hook callbacks
Pollux12 Jun 8, 2026
d4db618
Update CI
Pollux12 Jun 8, 2026
548ea4d
Restore override and support call_arg on override
Pollux12 Jun 9, 2026
47b4657
Fix annotation generation not including custom by default
Pollux12 Jun 10, 2026
d8e0b9b
Fix debug.getmetatable annotation
Pollux12 Jun 11, 2026
3eb045b
Add error annotation
Pollux12 Jun 12, 2026
d7a0cf5
Add IsHostingGame menu annotation
Pollux12 Jun 12, 2026
16e86a9
Fix PropertyAdd optional fields
Pollux12 Jun 12, 2026
5c5e83c
Fix scripted entity registration table type
Pollux12 Jun 12, 2026
39cad47
Fix VideoData optional lockfps
Pollux12 Jun 12, 2026
dbcb8c6
Add vgui.RegisterTable call metadata
Pollux12 Jun 12, 2026
4417a37
Add vgui.CreateFromTable call metadata
Pollux12 Jun 12, 2026
2039de6
Fix material proxy callback annotations
Pollux12 Jun 12, 2026
361cba1
Add vgui.RegisterFile call metadata
Pollux12 Jun 12, 2026
9cc3b55
Fix ViewData optional fields
Pollux12 Jun 12, 2026
ae88f49
Add engine entity and panel class annotations
Pollux12 Jun 12, 2026
8e678a4
Fix DMenu AddPanel panel parameter
Pollux12 Jun 12, 2026
b095693
Fix coerced checkbox and cookie annotations
Pollux12 Jun 12, 2026
091475c
Fix render clear target color annotation
Pollux12 Jun 12, 2026
fbdcd5f
Fix DProperty value changed annotation
Pollux12 Jun 12, 2026
7dea993
Fix panel helper annotations
Pollux12 Jun 12, 2026
2fb55cc
Fix PropertyAdd callback self annotation
Pollux12 Jun 12, 2026
6d7099b
Fix os.date return annotation
Pollux12 Jun 12, 2026
e8b2418
Fix VGUI optional reset annotations
Pollux12 Jun 12, 2026
d0345f3
Fix known engine entity create annotations
Pollux12 Jun 12, 2026
5d8bd3d
Fix SkeletonConvertor annotations
Pollux12 Jun 12, 2026
f96477f
Fix Derma skin annotations
Pollux12 Jun 12, 2026
3c3448f
Fix SkeletonConvertor callback annotations
Pollux12 Jun 12, 2026
ad8fb50
Fix phys_hinge engine entity annotation
Pollux12 Jun 12, 2026
41ef4cf
Fix widget axis entity annotations
Pollux12 Jun 12, 2026
74d9039
Fix Derma_Anim return annotation
Pollux12 Jun 12, 2026
488f6d0
Fix VGUI panel annotation overrides
Pollux12 Jun 12, 2026
537a8d5
Fix Vector color alias annotations
Pollux12 Jun 12, 2026
8f3b422
Fix workshop menu annotation overrides
Pollux12 Jun 12, 2026
89ce174
Fix DPropertySheet, DTab, and workshop file shape annotations
Pollux12 Jun 12, 2026
920f3db
refine DModelPanel field nullability
Pollux12 Jun 12, 2026
6e7c311
fix: remove false-positive params from UGCPublishWindow:DoPublish ann…
Pollux12 Jun 12, 2026
5b21fe6
Fix FormattedTime return overloads
Pollux12 Jun 13, 2026
659c9f2
Fix Player drive entity nil annotations
Pollux12 Jun 13, 2026
417f58c
Fix SetOnViewMaterial backup annotations
Pollux12 Jun 13, 2026
66c2ea0
Fix dragdrop receiver annotations
Pollux12 Jun 13, 2026
df3a979
Fix VGUI optional callback annotations
Pollux12 Jun 13, 2026
e2ccee0
Add low-risk runtime field annotations
Pollux12 Jun 13, 2026
79dde66
Add workshop singleton annotations
Pollux12 Jun 13, 2026
bfb0254
Fix DListView line panel column annotations
Pollux12 Jun 13, 2026
a51677b
Add string.Comma and util.GetSunInfo override annotations
Pollux12 Jun 13, 2026
aa46c07
Fix DTree_Node root and right-click annotations
Pollux12 Jun 13, 2026
3b2164d
Add Global.LoadPresets override annotation
Pollux12 Jun 14, 2026
220027e
Add DMenu:AddSpacer return override
Pollux12 Jun 14, 2026
a1e248f
Add drive and DrawBloom annotation overrides
Pollux12 Jun 14, 2026
f90a2cd
Add loader annotation overrides
Pollux12 Jun 16, 2026
de0b951
Add stable and prerelease annotation streams
Pollux12 Jun 20, 2026
9b6ced5
Merge branch 'main' into beta
Pollux12 Jun 21, 2026
014d153
Fix CI
Pollux12 Jun 21, 2026
96cb9cf
Fix CI
Pollux12 Jun 21, 2026
549f958
Fix CI
Pollux12 Jun 21, 2026
1557aab
Merge branch 'main' into beta
Pollux12 Jun 21, 2026
0feda2e
Merge branch 'beta' into base-lua-false-positives
Pollux12 Jun 21, 2026
2ff0a76
Add gamemode field annotation overrides
Pollux12 Jun 21, 2026
e7f0477
Add remaining base annotation overrides
Pollux12 Jun 21, 2026
dca8760
Add Derma field annotation overrides
Pollux12 Jun 21, 2026
e86bc14
Add shipped Lua false-positive annotation overrides
Pollux12 Jun 21, 2026
f4f524b
Route orphan overrides case-insensitively
Pollux12 Jun 22, 2026
b2bfecf
Change CreateFromTable call argument to table
Pollux12 Jun 22, 2026
15eb7df
Restore CreateFromTable register table call argument
Pollux12 Jun 23, 2026
8f3db74
Add NextBot loco field annotation
Pollux12 Jun 23, 2026
5468fd2
Add DesktopWindows list entry annotations
Pollux12 Jun 23, 2026
ce57af7
Add ToolObj class annotation
Pollux12 Jun 23, 2026
2b67741
Fix GetConVar nullable and widen BuildCPanel
Pollux12 Jun 23, 2026
5160cfc
Add missing class field annotations
Pollux12 Jun 23, 2026
06466c3
Fix various annotation overrides to be accurate to source
Pollux12 Jun 24, 2026
3097aec
Add guard metadata overrides
Pollux12 Jun 25, 2026
35f1b8f
Annotate ToolObj SetObject
Pollux12 Jun 25, 2026
94b98d8
Add owner-valid weapon callbacks
Pollux12 Jun 25, 2026
b74e505
Add sandbox derma and entity annotations
Pollux12 Jun 25, 2026
6ec3dc9
Mark IsValid as a validity guard
Pollux12 Jun 25, 2026
7e76396
Add DTVar overload and skin annotations
Pollux12 Jun 25, 2026
7245e27
Add prop_dynamic class annotation
Pollux12 Jun 25, 2026
e200cdc
Add DTree annotations
Pollux12 Jun 25, 2026
7c23a9f
Add base and sandbox annotation overrides
Pollux12 Jun 26, 2026
b0a8dba
Add ConVar guard metadata
Pollux12 Jun 26, 2026
9e0bac2
Add sandbox entity class annotations
Pollux12 Jun 27, 2026
4aae9da
Add owner guard to deploy and think callbacks
Pollux12 Jun 27, 2026
fbd0afe
Add winch controller constraint annotations
Pollux12 Jun 27, 2026
c4267d0
Fix various annotations based on lua source
Pollux12 Jun 29, 2026
7f7bdba
Add DPanelList and DPanelSelect annotations
Pollux12 Jul 2, 2026
5fe8ea0
Add side-effect-free color annotations
Pollux12 Jul 3, 2026
2154e0b
Add side-effect-free color and math annotations
Pollux12 Jul 3, 2026
9dada2b
Add ConVar write annotations
Pollux12 Jul 3, 2026
1e81474
Fix global IsValid guard return type
Pollux12 Jul 4, 2026
87006bd
Fix various annotations based on lua source
Pollux12 Jul 5, 2026
da58804
Fix Weapon:GetOwner return type
Pollux12 Jul 5, 2026
2a855ec
Remove "side-effect-free" annotation type
Pollux12 Jul 5, 2026
d3603bb
Preserve structure annotations during merge
Pollux12 Jul 11, 2026
667bf1a
Fix panel and player annotations
Pollux12 Jul 12, 2026
c52bdb4
Annotate VGUI parent relationships
Pollux12 Jul 12, 2026
68d494a
Remove self-reference
Pollux12 Jul 14, 2026
4839c71
Add base entities
Pollux12 Jul 15, 2026
b463032
Fix / add various annotations
Pollux12 Jul 16, 2026
63c3e1c
Add CompileFile annotations
Pollux12 Jul 16, 2026
92b73f0
Fix various annotations
Pollux12 Jul 16, 2026
b0482d3
Add debug.sethook override
Pollux12 Jul 16, 2026
a1262cb
Add isentity type guard
Pollux12 Jul 19, 2026
106d176
Fix panel hooks
Pollux12 Jul 20, 2026
90f79b8
Add sandbox gamemode annotations
Pollux12 Jul 20, 2026
67603fe
Fix assert and pairs
Pollux12 Jul 20, 2026
1f608e9
Add some missing VGUI annotations
Pollux12 Jul 20, 2026
c9c2296
Add various missing annotations
Pollux12 Jul 22, 2026
c91f1c9
Fix CI
Pollux12 Jul 24, 2026
4d87834
Fix CI
Pollux12 Jul 24, 2026
85b74bd
Merge pull request #3 from Pollux12/base-lua-false-positives
Pollux12 Jul 24, 2026
5f578ca
Fix CI
Pollux12 Jul 24, 2026
e6a0217
Fix release CI
Pollux12 Jul 24, 2026
61ed96e
Fix release CI test
Pollux12 Jul 24, 2026
3b82e83
Add annotation overrides for net message parsing
Pollux12 Jul 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/release-gluals.yml
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ jobs:
run: |
npm run generate-lua -- \
--output ./output \
--custom-overrides ./custom
-c ./custom
npm run generate-plugin-index
npm run generate-plugin-artifacts -- \
--pluginRoot ./plugin \
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ jobs:
node-version: "22"
- name: Install dependencies
run: npm ci
- name: Generate output fixtures
run: npm run scrape-wiki
- name: Run tests
run: npm run ci:test
- uses: coverallsapp/github-action@v2
24 changes: 24 additions & 0 deletions __tests__/api-writer/glua-api-writer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,30 @@ describe('GLua API Writer', () => {
expect(api).toContain('function GM:PlayerInitialSpawn(player, transition) end');
});

it('emits panel hooks as panel-owned callback contracts', () => {
const markup = `
<function name="OnNodeSelected" parent="DTree" type="panelhook">
<description>This function is called when a node within a tree is selected.</description>
<realm>Client and Menu</realm>
<args>
<arg name="node" type="Panel">The node that was selected.</arg>
</args>
</function>`;
const response = <Response>{
url: 'https://wiki.facepunch.com/gmod/DTree:OnNodeSelected?format=text',
};
const [page] = new WikiPageMarkupScraper(response.url).getScrapeCallback()(response, markup) as WikiPage[];
const writer = new GluaApiWriter();
writer.writePages([page], mockFilePath);
const api = writer.makeApiFromPages(writer.getPages(mockFilePath));

expect(api).toContain('---@hook OnNodeSelected');
expect(api).toContain('---@realm client');
expect(api).toContain('---@realm menu');
expect(api).toContain('---@param node Panel The node that was selected.');
expect(api).toContain('function DTree:OnNodeSelected(node) end');
});

it('should emit source and realm annotations when present', () => {
const writer = new GluaApiWriter();
const api = writer.writePage(<LibraryFunction>{
Expand Down
302 changes: 293 additions & 9 deletions __tests__/cli-generate-lua.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('cli-generate-lua', () => {
try {
const command = process.platform === 'win32' ? 'npm.cmd' : 'npm';
const result = spawnSync(
`${command} run generate-lua -- --output "${outputPath}" --customOverrides ./custom`,
`${command} run generate-lua -- --output "${outputPath}" --custom-overrides ./custom`,
[],
{
cwd: process.cwd(),
Expand All @@ -36,6 +36,148 @@ describe('cli-generate-lua', () => {
}
});

test('uses runtime-generic debug.getmetatable annotation override', () => {
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gluals-generate-lua-debug-getmetatable-'));
const outputPath = path.join(tmpRoot, 'output');
const customOverridesPath = path.join(tmpRoot, 'custom');
const debugDir = path.join(outputPath, 'debug');

fs.mkdirSync(debugDir, { recursive: true });
fs.mkdirSync(customOverridesPath, { recursive: true });

fs.copyFileSync(
path.join(process.cwd(), 'custom', 'debug.getmetatable.lua'),
path.join(customOverridesPath, 'debug.getmetatable.lua'),
);

const pagesPath = path.join(debugDir, 'getmetatable.json');
fs.writeFileSync(
pagesPath,
JSON.stringify([
{
type: 'libraryfunc',
parent: 'debug',
name: 'getmetatable',
address: 'debug.getmetatable',
description: 'Returns the metatable of the specified value.',
realm: 'shared',
url: 'https://wiki.facepunch.com/gmod/debug.getmetatable',
arguments: [
{
args: [
{
name: 'object',
type: 'any',
},
],
},
],
returns: [
{
type: 'any',
},
],
},
], null, 2),
'utf8',
);

try {
const command = process.platform === 'win32' ? 'npm.cmd' : 'npm';
const result = spawnSync(
`${command} run generate-lua -- --output "${outputPath}" --custom-overrides "${customOverridesPath}"`,
[],
{
cwd: process.cwd(),
encoding: 'utf8',
shell: true,
},
);

expect(result.status).toBe(0);
const debugLua = fs.readFileSync(path.join(outputPath, 'debug.lua'), 'utf8');
expect(debugLua).toContain('---@generic T');
expect(debugLua).toContain('---@param object T The value to get the metatable of.');
expect(debugLua).toContain('---@return (definition) T # The metatable of the value.');
expect(debugLua).not.toContain('`T`');
expect(debugLua).not.toContain('---@generic T : table');
} finally {
fs.rmSync(tmpRoot, { recursive: true, force: true });
}
});

test('emits custom function overrides for missing wiki pages', () => {
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gluals-generate-lua-missing-overrides-'));
const outputPath = path.join(tmpRoot, 'output');
const customOverridesPath = path.join(tmpRoot, 'custom');
const steamworksDir = path.join(outputPath, 'steamworks');

fs.mkdirSync(steamworksDir, { recursive: true });
fs.mkdirSync(customOverridesPath, { recursive: true });

fs.writeFileSync(
path.join(steamworksDir, 'library.json'),
JSON.stringify(
[
{
type: 'library',
address: 'steamworks',
name: 'steamworks',
description: 'Steamworks related functions.',
realm: 'shared',
url: 'https://wiki.facepunch.com/gmod/steamworks',
},
],
null,
2,
),
'utf8',
);

fs.writeFileSync(
path.join(customOverridesPath, 'steamworks.GetDownloadedItems.lua'),
[
'---Returns a list of downloaded UGC item IDs.',
'---@return string[]',
'function steamworks.GetDownloadedItems() end',
'',
].join('\n'),
'utf8',
);

fs.writeFileSync(
path.join(customOverridesPath, 'steamworks.FileUserInfo.lua'),
[
'---Retrieves local file/user data for a Steam Workshop addon.',
'---@param workshopItemID string',
'---@param callback fun(info: SteamworksFileUserInfo)',
'function steamworks.FileUserInfo(workshopItemID, callback) end',
'',
].join('\n'),
'utf8',
);

try {
const command = process.platform === 'win32' ? 'npm.cmd' : 'npm';
const result = spawnSync(
`${command} run generate-lua -- --output "${outputPath}" --custom-overrides "${customOverridesPath}"`,
[],
{
cwd: process.cwd(),
encoding: 'utf8',
shell: true,
},
);

expect(result.status).toBe(0);
const steamworksLua = fs.readFileSync(path.join(outputPath, 'steamworks.lua'), 'utf8');
expect(steamworksLua).toContain('function steamworks.GetDownloadedItems() end');
expect(steamworksLua).toContain('function steamworks.FileUserInfo(workshopItemID, callback) end');
} finally {
fs.rmSync(tmpRoot, { recursive: true, force: true });
}
});

test('applies typed Entity networked getter overrides', () => {
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gluals-generate-lua-'));
const outputPath = path.join(tmpRoot, 'output');
Expand Down Expand Up @@ -65,11 +207,15 @@ describe('cli-generate-lua', () => {
writeEntityGetterPage('getnwentity.json', 'GetNWEntity', 'any', 'NULL');
writeEntityGetterPage('getnwint.json', 'GetNWInt', 'any', '0');
writeEntityGetterPage('getnetworkedentity.json', 'GetNetworkedEntity', 'Entity', 'NULL');
writeEntityGetterPage('getnwbool.json', 'GetNWBool', 'any', 'false');
writeEntityGetterPage('getnwstring.json', 'GetNWString', 'any', '""');
writeEntityGetterPage('getnwvector.json', 'GetNWVector', 'any', 'Vector(0,0,0)');
writeEntityGetterPage('getnwangle.json', 'GetNWAngle', 'any', 'Angle(0,0,0)');

try {
const command = process.platform === 'win32' ? 'npm.cmd' : 'npm';
const result = spawnSync(
`${command} run generate-lua -- --output "${outputPath}" --customOverrides ./custom`,
`${command} run generate-lua -- --output "${outputPath}" --custom-overrides ./custom`,
[],
{
cwd: process.cwd(),
Expand All @@ -80,18 +226,156 @@ describe('cli-generate-lua', () => {

expect(result.status).toBe(0);
const entityLua = fs.readFileSync(path.join(outputPath, 'entity.lua'), 'utf8');
expect(entityLua).toContain('---@overload fun(self: Entity, key: string): Entity|NULL # The value associated with the key');
expect(entityLua).toContain('---@overload fun(self: Entity, key: string): number # The value associated with the key');
expect(entityLua).toContain('---@overload fun(self: Entity, key: string): Entity|NULL # The retrieved value');
expect(entityLua).toContain('---@param fallback T The value to return if we failed to retrieve the value.');
expect(entityLua).toContain('---@return Entity|T # The value associated with the key');
expect(entityLua).toContain('---@return number|T # The value associated with the key');
expect(entityLua).toContain('---@return Entity|T # The retrieved value');
const expectedGetters = [
{ name: 'GetNWEntity', overload: 'Entity|NULL', fallback: 'NULL', returns: 'Entity|T' },
{ name: 'GetNWInt', overload: 'number', fallback: '0', returns: 'number|T' },
{ name: 'GetNetworkedEntity', overload: 'Entity|NULL', fallback: 'NULL', returns: 'Entity|T' },
{ name: 'GetNWBool', overload: 'boolean', fallback: 'false', returns: 'boolean|T' },
{ name: 'GetNWString', overload: 'string', fallback: '""', returns: 'string|T' },
{ name: 'GetNWVector', overload: 'Vector', fallback: 'Vector( 0, 0, 0 )', returns: 'Vector|T' },
{ name: 'GetNWAngle', overload: 'Angle', fallback: 'Angle( 0, 0, 0 )', returns: 'Angle|T' },
];

for (const getter of expectedGetters) {
const block = entityLua.match(new RegExp(`---@source https://wiki\\.facepunch\\.com/gmod/Entity:${getter.name}[\\s\\S]*?function Entity:${getter.name}\\(key, fallback\\) end`))?.[0];
expect(block).toBeDefined();
expect(block).toContain(`---@overload fun(self: Entity, key: string): ${getter.overload}`);
expect(block).toContain(`---@param fallback? T=${getter.fallback}`);
expect(block).toContain(`---@return ${getter.returns}`);
}

expect(entityLua).not.toContain('---@param fallback? Entity');
expect(entityLua).not.toContain('---@param fallback? number');
expect(entityLua).not.toMatch(/---@param fallback\? T .*Defaults to/);
expect(entityLua).not.toContain('---@return any');
} finally {
fs.rmSync(tmpRoot, { recursive: true, force: true });
}
});

test('applies custom overrides by default when --custom-overrides is not specified', () => {
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gluals-generate-lua-defaults-'));
const outputPath = path.join(tmpRoot, 'output');
const vectorDir = path.join(outputPath, 'vector');

fs.mkdirSync(vectorDir, { recursive: true });
fs.writeFileSync(
path.join(vectorDir, 'pages.json'),
JSON.stringify([
{
type: 'class',
address: 'Vector',
name: 'Vector',
description: 'A 3D vector.',
realm: 'shared',
url: 'https://wiki.facepunch.com/gmod/Vector',
parent: '',
},
], null, 2),
'utf8',
);

try {
const command = process.platform === 'win32' ? 'npm.cmd' : 'npm';
const result = spawnSync(
`${command} run generate-lua -- --output "${outputPath}"`,
[],
{
cwd: process.cwd(),
encoding: 'utf8',
shell: true,
},
);

expect(result.status).toBe(0);
const vectorLua = fs.readFileSync(path.join(outputPath, 'vector.lua'), 'utf8');
// These lines come from custom/class.Vector.lua overrides
expect(vectorLua).toContain('---@field x number');
expect(vectorLua).toContain('---@field y number');
expect(vectorLua).toContain('---@field z number');
expect(vectorLua).toContain('---@operator add(Vector): Vector');
} finally {
fs.rmSync(tmpRoot, { recursive: true, force: true });
}
});

test('--raw-wiki skips applying custom overrides', () => {
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gluals-generate-lua-rawwiki-'));
const outputPath = path.join(tmpRoot, 'output');
const vectorDir = path.join(outputPath, 'vector');

fs.mkdirSync(vectorDir, { recursive: true });
fs.writeFileSync(
path.join(vectorDir, 'pages.json'),
JSON.stringify([
{
type: 'class',
address: 'Vector',
name: 'Vector',
description: 'A 3D vector.',
realm: 'shared',
url: 'https://wiki.facepunch.com/gmod/Vector',
parent: '',
},
], null, 2),
'utf8',
);

try {
const command = process.platform === 'win32' ? 'npm.cmd' : 'npm';
const result = spawnSync(
`${command} run generate-lua -- --output "${outputPath}" --raw-wiki`,
[],
{
cwd: process.cwd(),
encoding: 'utf8',
shell: true,
},
);

expect(result.status).toBe(0);
const vectorLua = fs.readFileSync(path.join(outputPath, 'vector.lua'), 'utf8');
// With --raw-wiki, custom overrides are skipped so @field/@operator from custom/class.Vector.lua should NOT appear
expect(vectorLua).not.toContain('---@field x number');
expect(vectorLua).not.toContain('---@operator add(Vector): Vector');
} finally {
fs.rmSync(tmpRoot, { recursive: true, force: true });
}
});

test('--no-wipe-lua preserves existing Lua files', () => {
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gluals-generate-lua-nowipe-'));
const outputPath = path.join(tmpRoot, 'output');
const vectorDir = path.join(outputPath, 'vector');

fs.mkdirSync(vectorDir, { recursive: true });
fs.writeFileSync(
path.join(vectorDir, 'pages.json'),
JSON.stringify([], null, 2),
'utf8',
);

// Write a sentinel file that should survive when wipe is disabled
const sentinelFile = path.join(outputPath, 'sentinel.lua');
fs.writeFileSync(sentinelFile, '-- sentinel', 'utf8');

try {
const command = process.platform === 'win32' ? 'npm.cmd' : 'npm';
const result = spawnSync(
`${command} run generate-lua -- --output "${outputPath}" --no-wipe-lua --raw-wiki`,
[],
{
cwd: process.cwd(),
encoding: 'utf8',
shell: true,
},
);

expect(result.status).toBe(0);
expect(fs.existsSync(sentinelFile)).toBe(true);
expect(fs.readFileSync(sentinelFile, 'utf8')).toBe('-- sentinel');
} finally {
fs.rmSync(tmpRoot, { recursive: true, force: true });
}
});
});
Loading
Loading