diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml new file mode 100644 index 00000000..80d77eff --- /dev/null +++ b/.github/workflows/node.yml @@ -0,0 +1,51 @@ +name: Node.js CI + +on: + push: + pull_request: + +env: + # The packages use workspace:^ and catalog: versions, so they only resolve + # inside a clusterio checkout with this repository under external_plugins + CLUSTERIO_REPOSITORY: clusterio/clusterio + CLUSTERIO_REF: master + # Node version to use for the tests. This is used by the setup-node action + NODE_VERSION: lts/* + +jobs: + test: + runs-on: ubuntu-latest + timeout-minutes: 5 + defaults: + run: + working-directory: external_plugins/ExpCluster + steps: + - name: Checkout Clusterio + uses: actions/checkout@v4 + with: + repository: ${{ env.CLUSTERIO_REPOSITORY }} + ref: ${{ env.CLUSTERIO_REF }} + - name: Checkout ExpCluster + uses: actions/checkout@v4 + with: + path: external_plugins/ExpCluster + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + - name: Install and build + working-directory: . + run: pnpm install --no-frozen-lockfile + - name: Run tests + run: pnpm --filter "@expcluster/*" run test + - name: Run reference checks + run: | + # pnpm install runs prepare in dependency order, which hides missing + # project references. Building from an empty dist one package at a time + # will fail unless every cross package import is referenced correctly. + for pkg in exp_*/; do + rm -rf exp_*/dist + echo "::group::${pkg%/}" + (cd "$pkg" && pnpm exec tsc --build) + echo "::endgroup::" + done diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9fa851c4..216a5196 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ For developers wanting to add features please follow these guidelines: - Lua is checked with [emmylua](https://github.com/EmmyLuaLs/emmylua-analyzer-rust), configured by `.emmyrc.json`. Install the EmmyLua extension rather than sumnekolua / luals, and use factoriomod-debug to generate the factorio api types. Note that an inline cast must be written `--[[@as T]]`, the spaced form is not parsed. - Changes should be made on your own fork and merged into `main` through a pull request. - Each pull request should be limited to one feature or a few bug fixes and link to the related issue page. -- Pull requests are automatically linted and documentation checked. +- Pull requests are automatically linted, then built and tested against the latest clusterio `master`. - Pull requests are manually reviewed to maintain code and language quality. - New features should have the branch names: `feature/feature-name` - Bug fixes should have the branch names: `fix/bug-name` diff --git a/exp_commands/index.ts b/exp_commands/index.ts index 3e788480..58d11b1f 100644 --- a/exp_commands/index.ts +++ b/exp_commands/index.ts @@ -4,5 +4,5 @@ export const plugin: lib.PluginDeclaration = { name: "exp_commands", title: "exp_commands", description: "Example Description. Plugin. Change me in index.ts", - instanceEntrypoint: "./dist/node/instance", + instanceEntrypoint: "./dist/node/instance.js", }; diff --git a/exp_commands/package.json b/exp_commands/package.json index 7628cfeb..04b5f922 100644 --- a/exp_commands/package.json +++ b/exp_commands/package.json @@ -5,6 +5,7 @@ "author": "Cooldude2606 ", "license": "MIT", "repository": "explosivegaming/ExpCluster", + "type": "module", "main": "dist/node/index.js", "scripts": { "prepare": "tsc --build && webpack-cli --env production" diff --git a/exp_commands/webpack.config.js b/exp_commands/webpack.config.cjs similarity index 100% rename from exp_commands/webpack.config.js rename to exp_commands/webpack.config.cjs diff --git a/exp_groups/controller.ts b/exp_groups/controller.ts index 14d13ef2..a6034433 100644 --- a/exp_groups/controller.ts +++ b/exp_groups/controller.ts @@ -1,6 +1,6 @@ import { BaseControllerPlugin } from "@clusterio/controller"; import * as lib from "@clusterio/lib"; -import * as messages from "./messages"; +import * as messages from "./messages.js"; import * as path from "node:path"; export class ControllerPlugin extends BaseControllerPlugin { diff --git a/exp_groups/index.ts b/exp_groups/index.ts index 743f33b9..c92c3e6b 100644 --- a/exp_groups/index.ts +++ b/exp_groups/index.ts @@ -1,8 +1,8 @@ import * as lib from "@clusterio/lib"; -import * as messages from "./messages"; +import * as messages from "./messages.js"; -export * from "./messages"; -export type { ControllerPlugin } from "./controller"; +export * from "./messages.js"; +export type { ControllerPlugin } from "./controller.js"; declare module "@clusterio/lib" { export interface InstanceConfigFields { @@ -12,123 +12,6 @@ declare module "@clusterio/lib" { } } -// Group permissions - -lib.definePermission({ - name: "exp_groups.group.get", - title: "Get Groups", - description: "Retrieve a specific Factorio permission group by ID.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.group.list", - title: "List Groups", - description: "List all Factorio permission groups.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.group.subscribe", - title: "Subscribe to Group Updates", - description: "Receive updates when Factorio permission groups change.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.group.create", - title: "Create Groups", - description: "Create new Factorio permission groups.", - grantByDefault: false, -}); -lib.definePermission({ - name: "exp_groups.group.update", - title: "Update Groups", - description: "Modify existing Factorio permission groups.", - grantByDefault: false, -}); -lib.definePermission({ - name: "exp_groups.group.delete", - title: "Delete Groups", - description: "Delete Factorio permission groups.", - grantByDefault: false, -}); - -// Assignment permissions - -lib.definePermission({ - name: "exp_groups.assignment.get", - title: "Get Assignments", - description: "Retrieve a specific manual group assignment for a player.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.assignment.list", - title: "List Assignments", - description: "List all manual group assignments.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.assignment.subscribe", - title: "Subscribe to Assignment Updates", - description: "Receive updates when manual group assignments change.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.assignment.create", - title: "Create Assignments", - description: "Manually assign players to groups, overriding role mappings.", - grantByDefault: false, -}); -lib.definePermission({ - name: "exp_groups.assignment.update", - title: "Update Assignments", - description: "Modify existing manual group assignments.", - grantByDefault: false, -}); -lib.definePermission({ - name: "exp_groups.assignment.delete", - title: "Delete Assignments", - description: "Remove manual group assignments.", - grantByDefault: false, -}); - -// Role mapping permissions - -lib.definePermission({ - name: "exp_groups.role_mapping.get", - title: "Get Role Mappings", - description: "Retrieve a specific role mapping rule.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.role_mapping.list", - title: "List Role Mappings", - description: "List all role mapping rules.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.role_mapping.subscribe", - title: "Subscribe to Role Mapping Updates", - description: "Receive updates when role mapping rules change.", - grantByDefault: true, -}); -lib.definePermission({ - name: "exp_groups.role_mapping.create", - title: "Create Role Mappings", - description: "Create rules that map user roles to Factorio permission groups.", - grantByDefault: false, -}); -lib.definePermission({ - name: "exp_groups.role_mapping.update", - title: "Update Role Mappings", - description: "Modify existing role mapping rules.", - grantByDefault: false, -}); -lib.definePermission({ - name: "exp_groups.role_mapping.delete", - title: "Delete Role Mappings", - description: "Delete role mapping rules.", - grantByDefault: false, -}); - export const plugin: lib.PluginDeclaration = { name: "exp_groups", title: "ExpGaming - Permission Groups", @@ -164,7 +47,126 @@ export const plugin: lib.PluginDeclaration = { messages.RoleMappingListRequest, ], - instanceEntrypoint: "./dist/node/instance", + permissions: [ + // Group permissions + + { + name: "exp_groups.group.get", + title: "Get Groups", + description: "Retrieve a specific Factorio permission group by ID.", + grantByDefault: true, + }, + { + name: "exp_groups.group.list", + title: "List Groups", + description: "List all Factorio permission groups.", + grantByDefault: true, + }, + { + name: "exp_groups.group.subscribe", + title: "Subscribe to Group Updates", + description: "Receive updates when Factorio permission groups change.", + grantByDefault: true, + }, + { + name: "exp_groups.group.create", + title: "Create Groups", + description: "Create new Factorio permission groups.", + grantByDefault: false, + }, + { + name: "exp_groups.group.update", + title: "Update Groups", + description: "Modify existing Factorio permission groups.", + grantByDefault: false, + }, + { + name: "exp_groups.group.delete", + title: "Delete Groups", + description: "Delete Factorio permission groups.", + grantByDefault: false, + }, + + // Assignment permissions + + { + name: "exp_groups.assignment.get", + title: "Get Assignments", + description: "Retrieve a specific manual group assignment for a player.", + grantByDefault: true, + }, + { + name: "exp_groups.assignment.list", + title: "List Assignments", + description: "List all manual group assignments.", + grantByDefault: true, + }, + { + name: "exp_groups.assignment.subscribe", + title: "Subscribe to Assignment Updates", + description: "Receive updates when manual group assignments change.", + grantByDefault: true, + }, + { + name: "exp_groups.assignment.create", + title: "Create Assignments", + description: "Manually assign players to groups, overriding role mappings.", + grantByDefault: false, + }, + { + name: "exp_groups.assignment.update", + title: "Update Assignments", + description: "Modify existing manual group assignments.", + grantByDefault: false, + }, + { + name: "exp_groups.assignment.delete", + title: "Delete Assignments", + description: "Remove manual group assignments.", + grantByDefault: false, + }, + + // Role mapping permissions + + { + name: "exp_groups.role_mapping.get", + title: "Get Role Mappings", + description: "Retrieve a specific role mapping rule.", + grantByDefault: true, + }, + { + name: "exp_groups.role_mapping.list", + title: "List Role Mappings", + description: "List all role mapping rules.", + grantByDefault: true, + }, + { + name: "exp_groups.role_mapping.subscribe", + title: "Subscribe to Role Mapping Updates", + description: "Receive updates when role mapping rules change.", + grantByDefault: true, + }, + { + name: "exp_groups.role_mapping.create", + title: "Create Role Mappings", + description: "Create rules that map user roles to Factorio permission groups.", + grantByDefault: false, + }, + { + name: "exp_groups.role_mapping.update", + title: "Update Role Mappings", + description: "Modify existing role mapping rules.", + grantByDefault: false, + }, + { + name: "exp_groups.role_mapping.delete", + title: "Delete Role Mappings", + description: "Delete role mapping rules.", + grantByDefault: false, + }, + ], + + instanceEntrypoint: "./dist/node/instance.js", instanceConfigFields: { "exp_groups.sync_mode": { description: "Synchronize permission groups with the controller", @@ -174,7 +176,7 @@ export const plugin: lib.PluginDeclaration = { }, }, - controllerEntrypoint: "./dist/node/controller", + controllerEntrypoint: "./dist/node/controller.js", controllerConfigFields: { }, diff --git a/exp_groups/instance.ts b/exp_groups/instance.ts index d3ed3cc4..207b969c 100644 --- a/exp_groups/instance.ts +++ b/exp_groups/instance.ts @@ -1,6 +1,6 @@ import { BaseInstancePlugin } from "@clusterio/host"; import * as lib from "@clusterio/lib"; -import * as messages from "./messages"; +import * as messages from "./messages.js"; export type IpcGroupUpdated = { group_name: string, diff --git a/exp_groups/messages.ts b/exp_groups/messages.ts index b0e5e986..d5191ca9 100644 --- a/exp_groups/messages.ts +++ b/exp_groups/messages.ts @@ -1,6 +1,30 @@ import * as lib from "@clusterio/lib"; import { Type, Static } from "@sinclair/typebox"; +// Declared here rather than in index.ts because the browser project cannot include index.ts, which re-exports controller.ts +declare module "@clusterio/lib" { + export interface Permissions { + "exp_groups.group.get": never; + "exp_groups.group.list": never; + "exp_groups.group.subscribe": never; + "exp_groups.group.create": never; + "exp_groups.group.update": never; + "exp_groups.group.delete": never; + "exp_groups.assignment.get": never; + "exp_groups.assignment.list": never; + "exp_groups.assignment.subscribe": never; + "exp_groups.assignment.create": never; + "exp_groups.assignment.update": never; + "exp_groups.assignment.delete": never; + "exp_groups.role_mapping.get": never; + "exp_groups.role_mapping.list": never; + "exp_groups.role_mapping.subscribe": never; + "exp_groups.role_mapping.create": never; + "exp_groups.role_mapping.update": never; + "exp_groups.role_mapping.delete": never; + } +} + /* Data records */ diff --git a/exp_groups/package.json b/exp_groups/package.json index 0bd0dae8..6e055307 100644 --- a/exp_groups/package.json +++ b/exp_groups/package.json @@ -5,6 +5,7 @@ "author": "Cooldude2606 ", "license": "MIT", "repository": "explosivegaming/ExpCluster", + "type": "module", "main": "dist/node/index.js", "scripts": { "prepare": "tsc --build && webpack-cli --env production" diff --git a/exp_groups/web/components/AssignmentForm.tsx b/exp_groups/web/components/AssignmentForm.tsx index 41a6a8f2..f20b21a2 100644 --- a/exp_groups/web/components/AssignmentForm.tsx +++ b/exp_groups/web/components/AssignmentForm.tsx @@ -2,7 +2,7 @@ import React, { useContext, useEffect } from "react"; import { Modal, Form, Select } from "antd"; import { ControlContext, useUsers } from "@clusterio/web_ui"; -import * as messages from "../../messages"; +import * as messages from "../../messages.js"; import type { WebPlugin } from ".."; export default function AssignmentForm({ open, setOpen, initial }: { diff --git a/exp_groups/web/components/AssignmentsTable.tsx b/exp_groups/web/components/AssignmentsTable.tsx index 680d9f78..f6997811 100644 --- a/exp_groups/web/components/AssignmentsTable.tsx +++ b/exp_groups/web/components/AssignmentsTable.tsx @@ -3,7 +3,7 @@ import { Table, Button, Space, Input, InputRef } from "antd"; import { EditOutlined, SearchOutlined } from "@ant-design/icons"; import { ControlContext, useAccount } from "@clusterio/web_ui"; -import { AssignmentDeleteRequest, AssignmentRecord } from "../../messages"; +import { AssignmentDeleteRequest, AssignmentRecord } from "../../messages.js"; import type { WebPlugin } from ".."; import AssignmentForm from "./AssignmentForm"; diff --git a/exp_groups/web/components/GroupForm.tsx b/exp_groups/web/components/GroupForm.tsx index 14e3aa1a..945f3c59 100644 --- a/exp_groups/web/components/GroupForm.tsx +++ b/exp_groups/web/components/GroupForm.tsx @@ -3,7 +3,7 @@ import { Modal, Form, Input, Switch } from "antd"; import { useNavigate } from "react-router-dom"; import { ControlContext } from "@clusterio/web_ui"; -import * as messages from "../../messages"; +import * as messages from "../../messages.js"; export default function GroupForm({ open, setOpen }: { open: boolean, diff --git a/exp_groups/web/components/GroupViewPage.tsx b/exp_groups/web/components/GroupViewPage.tsx index 1d5d31fe..1f5ec8ac 100644 --- a/exp_groups/web/components/GroupViewPage.tsx +++ b/exp_groups/web/components/GroupViewPage.tsx @@ -5,7 +5,7 @@ import { Button, Checkbox, Input, Space, Spin, Alert } from "antd"; import { ControlContext, useAccount, useDefaultModPack, PageLayout, PageHeader, notifyErrorHandler } from "@clusterio/web_ui"; import DeletedConfirm from "./DeleteConfirm"; -import * as messages from "../../messages"; +import * as messages from "../../messages.js"; import type { WebPlugin } from ".."; const DOMAIN_MAPPING = { diff --git a/exp_groups/web/components/GroupsTable.tsx b/exp_groups/web/components/GroupsTable.tsx index 1dc711d9..d3f6c9b9 100644 --- a/exp_groups/web/components/GroupsTable.tsx +++ b/exp_groups/web/components/GroupsTable.tsx @@ -3,7 +3,7 @@ import { Table } from "antd"; import { useNavigate } from "react-router-dom"; import { ControlContext } from "@clusterio/web_ui"; -import { GroupRecord } from "../../messages"; +import { GroupRecord } from "../../messages.js"; import type { WebPlugin } from ".."; import GroupForm from "./GroupForm"; diff --git a/exp_groups/web/components/RoleMappingForm.tsx b/exp_groups/web/components/RoleMappingForm.tsx index ee143301..59918b29 100644 --- a/exp_groups/web/components/RoleMappingForm.tsx +++ b/exp_groups/web/components/RoleMappingForm.tsx @@ -2,7 +2,7 @@ import React, { useContext, useEffect } from "react"; import { Modal, Form, Select, InputNumber, Switch, Alert } from "antd"; import { ControlContext, useRoles } from "@clusterio/web_ui"; -import { RoleMappingRecord, RoleMappingCreateRequest, RoleMappingUpdateRequest } from "../../messages"; +import { RoleMappingRecord, RoleMappingCreateRequest, RoleMappingUpdateRequest } from "../../messages.js"; import type { WebPlugin } from ".."; export default function RoleMappingForm({ open, setOpen, initial }: { diff --git a/exp_groups/web/components/RoleMappingsTable.tsx b/exp_groups/web/components/RoleMappingsTable.tsx index 05877426..ab948960 100644 --- a/exp_groups/web/components/RoleMappingsTable.tsx +++ b/exp_groups/web/components/RoleMappingsTable.tsx @@ -3,7 +3,7 @@ import { Table, Button, Space, Tag } from "antd"; import { EditOutlined } from "@ant-design/icons"; import { ControlContext, useAccount, useRoles } from "@clusterio/web_ui"; -import { RoleMappingRecord, RoleMappingDeleteRequest } from "../../messages"; +import { RoleMappingRecord, RoleMappingDeleteRequest } from "../../messages.js"; import type { WebPlugin } from ".."; import RoleMappingForm from "./RoleMappingForm"; diff --git a/exp_groups/web/index.tsx b/exp_groups/web/index.tsx index e9fbc797..5a2f24cd 100644 --- a/exp_groups/web/index.tsx +++ b/exp_groups/web/index.tsx @@ -2,7 +2,7 @@ import React, { useState, useCallback, useSyncExternalStore } from "react"; import { BaseWebPlugin, PageLayout, PageHeader, useAccount, SectionHeader } from "@clusterio/web_ui"; import { Button } from "antd"; -import * as messages from "../messages"; +import * as messages from "../messages.js"; import * as lib from "@clusterio/lib"; import GroupsTable from "./components/GroupsTable"; diff --git a/exp_groups/webpack.config.js b/exp_groups/webpack.config.cjs similarity index 100% rename from exp_groups/webpack.config.js rename to exp_groups/webpack.config.cjs diff --git a/exp_gui/index.ts b/exp_gui/index.ts index eb9596f4..b25ce074 100644 --- a/exp_gui/index.ts +++ b/exp_gui/index.ts @@ -4,5 +4,5 @@ export const plugin: lib.PluginDeclaration = { name: "exp_gui", title: "exp_gui", description: "Example Description. Plugin. Change me in index.ts", - instanceEntrypoint: "./dist/node/instance", + instanceEntrypoint: "./dist/node/instance.js", }; diff --git a/exp_gui/package.json b/exp_gui/package.json index 6679512a..5f344d11 100644 --- a/exp_gui/package.json +++ b/exp_gui/package.json @@ -5,6 +5,7 @@ "author": "Cooldude2606 ", "license": "MIT", "repository": "explosivegaming/ExpCluster", + "type": "module", "main": "dist/node/index.js", "scripts": { "prepare": "tsc --build && webpack-cli --env production" diff --git a/exp_gui/webpack.config.js b/exp_gui/webpack.config.cjs similarity index 100% rename from exp_gui/webpack.config.js rename to exp_gui/webpack.config.cjs diff --git a/exp_legacy/index.ts b/exp_legacy/index.ts index ff35fce5..c49d3201 100644 --- a/exp_legacy/index.ts +++ b/exp_legacy/index.ts @@ -4,5 +4,5 @@ export const plugin: lib.PluginDeclaration = { name: "exp_legacy", title: "exp_legacy", description: "Example Description. Plugin. Change me in index.ts", - instanceEntrypoint: "./dist/node/instance", + instanceEntrypoint: "./dist/node/instance.js", }; diff --git a/exp_legacy/package.json b/exp_legacy/package.json index f5220dc7..601368c5 100644 --- a/exp_legacy/package.json +++ b/exp_legacy/package.json @@ -5,6 +5,7 @@ "author": "Cooldude2606 ", "license": "MIT", "repository": "explosivegaming/ExpCluster", + "type": "module", "main": "dist/node/index.js", "scripts": { "prepare": "tsc --build && webpack-cli --env production" diff --git a/exp_legacy/webpack.config.js b/exp_legacy/webpack.config.cjs similarity index 100% rename from exp_legacy/webpack.config.js rename to exp_legacy/webpack.config.cjs diff --git a/exp_roles/controller.ts b/exp_roles/controller.ts index b9bf3a8b..96acc167 100644 --- a/exp_roles/controller.ts +++ b/exp_roles/controller.ts @@ -1,6 +1,6 @@ import { BaseControllerPlugin, InstanceRecord } from "@clusterio/controller"; import * as lib from "@clusterio/lib"; -import * as messages from "./messages"; +import * as messages from "./messages.js"; import * as path from "node:path"; export class ControllerPlugin extends BaseControllerPlugin { diff --git a/exp_roles/index.ts b/exp_roles/index.ts index e481fc8f..211f29c8 100644 --- a/exp_roles/index.ts +++ b/exp_roles/index.ts @@ -1,8 +1,8 @@ import * as lib from "@clusterio/lib"; -import * as messages from "./messages"; +import * as messages from "./messages.js"; -export * from "./messages"; -export type { ControllerPlugin } from "./controller"; +export * from "./messages.js"; +export type { ControllerPlugin } from "./controller.js"; declare module "@clusterio/lib" { export interface InstanceConfigFields { @@ -35,7 +35,7 @@ export const plugin: lib.PluginDeclaration = { messages.AssignmentUpdateRequest, ], - instanceEntrypoint: "./dist/node/instance", + instanceEntrypoint: "./dist/node/instance.js", instanceConfigFields: { "exp_roles.sync_mode": { description: "Synchronize in game roles with the controller", @@ -45,7 +45,7 @@ export const plugin: lib.PluginDeclaration = { }, }, - controllerEntrypoint: "./dist/node/controller", + controllerEntrypoint: "./dist/node/controller.js", controllerConfigFields: { }, diff --git a/exp_roles/instance.ts b/exp_roles/instance.ts index 208a0982..d57a5819 100644 --- a/exp_roles/instance.ts +++ b/exp_roles/instance.ts @@ -1,6 +1,6 @@ import { BaseInstancePlugin } from "@clusterio/host"; import * as lib from "@clusterio/lib"; -import * as messages from "./messages"; +import * as messages from "./messages.js"; /** Sent by the lua side when roles are changed in game. */ export type IpcAssignmentUpdate = { diff --git a/exp_roles/package.json b/exp_roles/package.json index c5f3ae57..6a09a372 100644 --- a/exp_roles/package.json +++ b/exp_roles/package.json @@ -5,10 +5,11 @@ "author": "Cooldude2606 ", "license": "MIT", "repository": "explosivegaming/ExpCluster", + "type": "module", "main": "dist/node/index.js", "scripts": { "prepare": "tsc --build && webpack-cli --env production", - "test": "tap --disable-coverage --allow-empty-coverage test/*.test.js", + "test": "tap --type-strip-only --disable-coverage --allow-empty-coverage test/*.test.js", "coverage": "tap --coverage-report=text test/*.test.js" }, "engines": { diff --git a/exp_roles/test/controller.test.js b/exp_roles/test/controller.test.js index 4e66ee48..0549c733 100644 --- a/exp_roles/test/controller.test.js +++ b/exp_roles/test/controller.test.js @@ -1,9 +1,8 @@ -"use strict"; -const t = require("tap"); -const lib = require("@clusterio/lib"); -const { Controller } = require("@clusterio/controller"); -const { ControllerPlugin } = require("../dist/node/controller"); -const messages = require("../dist/node/messages"); +import t from "tap"; +import * as lib from "@clusterio/lib"; +import { Controller } from "@clusterio/controller"; +import { ControllerPlugin } from "../dist/node/controller.js"; +import * as messages from "../dist/node/messages.js"; // The controller validates message classes against the link registry lib.Link.register(messages.RoleUpdatedEvent); diff --git a/exp_roles/test/instance.test.js b/exp_roles/test/instance.test.js index cb3db308..e0651358 100644 --- a/exp_roles/test/instance.test.js +++ b/exp_roles/test/instance.test.js @@ -1,10 +1,9 @@ -"use strict"; -const t = require("tap"); -const lib = require("@clusterio/lib"); -const { Instance } = require("@clusterio/host"); -const { InstancePlugin } = require("../dist/node/instance"); -const { plugin: pluginDeclaration } = require("../dist/node/index"); -const messages = require("../dist/node/messages"); +import t from "tap"; +import * as lib from "@clusterio/lib"; +import { Instance } from "@clusterio/host"; +import { InstancePlugin } from "../dist/node/instance.js"; +import { plugin as pluginDeclaration } from "../dist/node/index.js"; +import * as messages from "../dist/node/messages.js"; // The instance validates message classes against the link registry, and the // plugin's config fields must be defined before an InstanceConfig can set them diff --git a/exp_roles/test/messages.test.js b/exp_roles/test/messages.test.js index 8ec26550..324d0719 100644 --- a/exp_roles/test/messages.test.js +++ b/exp_roles/test/messages.test.js @@ -1,7 +1,6 @@ -"use strict"; -const t = require("tap"); -const messages = require("../dist/node/messages"); -const { testMatrix, testRoundTripJsonSerialisable } = require("../../test/common"); +import t from "tap"; +import * as messages from "../dist/node/messages.js"; +import { testMatrix, testRoundTripJsonSerialisable } from "../../test/common.js"; const fullMeta = new messages.RoleMetaRecord( 7, 3, 1, "Mod", "[Mod]", new messages.RoleColor(1, 2, 3), 3600000, true, 12345, false, diff --git a/exp_roles/test/module.test.js b/exp_roles/test/module.test.js index 2e5d4aa5..0acdfc29 100644 --- a/exp_roles/test/module.test.js +++ b/exp_roles/test/module.test.js @@ -1,14 +1,13 @@ -"use strict"; -const path = require("node:path"); -const t = require("tap"); -const { reportLuaTests } = require("../../test/lua/runner"); +import path from "node:path"; +import t from "tap"; +import { reportLuaTests } from "../../test/lua/runner.js"; -const envFile = path.join(__dirname, "module", "env.lua"); +const envFile = path.join(import.meta.dirname, "module", "env.lua"); // Each file runs in its own lua state, and each test in a fresh environment t.test("control.lua", t2 => { for (const file of ["lookup.lua", "players.lua", "assignment.lua", "sync.lua", "holders.lua"]) { - t2.test(file, t3 => reportLuaTests(t3, envFile, path.join(__dirname, "module", file))); + t2.test(file, t3 => reportLuaTests(t3, envFile, path.join(import.meta.dirname, "module", file))); } t2.end(); }); diff --git a/exp_roles/web/components/RoleProperties.tsx b/exp_roles/web/components/RoleProperties.tsx index b63ae707..53f6ca99 100644 --- a/exp_roles/web/components/RoleProperties.tsx +++ b/exp_roles/web/components/RoleProperties.tsx @@ -4,7 +4,7 @@ import { Button, Col, ColorPicker, Form, Input, InputNumber, Row, Switch, Toolti import * as lib from "@clusterio/lib"; import { ControlContext, SectionHeader, useAccount, notifyErrorHandler } from "@clusterio/web_ui"; -import { RoleColor, RoleMetaRecord, RoleMetaUpdateRequest } from "../../messages"; +import { RoleColor, RoleMetaRecord, RoleMetaUpdateRequest } from "../../messages.js"; import type { WebPlugin } from ".."; const MS_PER_HOUR = 3600000; diff --git a/exp_roles/web/index.tsx b/exp_roles/web/index.tsx index 644cd10d..36e9de1d 100644 --- a/exp_roles/web/index.tsx +++ b/exp_roles/web/index.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useSyncExternalStore } from "react"; import { BaseWebPlugin } from "@clusterio/web_ui"; import * as lib from "@clusterio/lib"; -import * as messages from "../messages"; +import * as messages from "../messages.js"; import RoleProperties from "./components/RoleProperties"; diff --git a/exp_roles/webpack.config.js b/exp_roles/webpack.config.cjs similarity index 100% rename from exp_roles/webpack.config.js rename to exp_roles/webpack.config.cjs diff --git a/exp_scenario/controller.ts b/exp_scenario/controller.ts index cd04b3db..05d3303e 100644 --- a/exp_scenario/controller.ts +++ b/exp_scenario/controller.ts @@ -4,8 +4,8 @@ import { RoleMetaRecord, type ControllerPlugin as RolesPlugin } from "@expcluste import { GroupRecord, GroupPermissions, RoleMappingRecord, type ControllerPlugin as GroupsPlugin, } from "@expcluster/permission-groups"; -import * as messages from "./messages"; -import { SeedRole, SeedGroup, seedRoles, seedGroups, flattenSeedPermissions } from "./seed"; +import * as messages from "./messages.js"; +import { SeedRole, SeedGroup, seedRoles, seedGroups, flattenSeedPermissions } from "./seed.js"; export class ControllerPlugin extends BaseControllerPlugin { async init() { diff --git a/exp_scenario/index.ts b/exp_scenario/index.ts index f09c3c6f..203c5721 100644 --- a/exp_scenario/index.ts +++ b/exp_scenario/index.ts @@ -1,41 +1,45 @@ import * as lib from "@clusterio/lib"; -import * as messages from "./messages"; - -// Defines a permission for every in game action and role flag used by the scenario -import "./permissions"; - -lib.definePermission({ - name: "exp_scenario.config.view", - title: "View ExpScenario Config", - description: "View the config for all submodules of ExpScenario", -}); - -lib.definePermission({ - name: "exp_scenario.config.edit", - title: "Edit ExpScenario Config", - description: "Edit the config for all submodules of ExpScenario", -}); - -lib.definePermission({ - name: "exp_scenario.seed", - title: "Seed ExpScenario roles and groups", - description: "Create the roles and permission groups the scenario shipped with", -}); +import * as messages from "./messages.js"; +import { permissions as scenarioPermissions, type ScenarioPermissionName } from "./permissions.js"; declare module "@clusterio/lib" { - + // Everything checked in game through exp_roles, plus the permissions checked on the controller + export interface Permissions extends Record { + "exp_scenario.config.view": never; + "exp_scenario.config.edit": never; + "exp_scenario.seed": never; + } } export const plugin: lib.PluginDeclaration = { name: "exp_scenario", title: "exp_scenario", description: "Example Description. Plugin. Change me in index.ts", - controllerEntrypoint: "./dist/node/controller", - instanceEntrypoint: "./dist/node/instance", + controllerEntrypoint: "./dist/node/controller.js", + instanceEntrypoint: "./dist/node/instance.js", messages: [ messages.SeedRequest, ], + permissions: [ + ...scenarioPermissions, + { + name: "exp_scenario.config.view", + title: "View ExpScenario Config", + description: "View the config for all submodules of ExpScenario", + }, + { + name: "exp_scenario.config.edit", + title: "Edit ExpScenario Config", + description: "Edit the config for all submodules of ExpScenario", + }, + { + name: "exp_scenario.seed", + title: "Seed ExpScenario roles and groups", + description: "Create the roles and permission groups the scenario shipped with", + }, + ], + webEntrypoint: "./web", }; diff --git a/exp_scenario/package.json b/exp_scenario/package.json index 1e4d9d65..706fef28 100644 --- a/exp_scenario/package.json +++ b/exp_scenario/package.json @@ -5,10 +5,11 @@ "author": "Cooldude2606 ", "license": "MIT", "repository": "explosivegaming/ExpCluster", + "type": "module", "main": "dist/node/index.js", "scripts": { "prepare": "tsc --build && webpack-cli --env production", - "test": "tap --disable-coverage --allow-empty-coverage test/*.test.js", + "test": "tap --type-strip-only --disable-coverage --allow-empty-coverage test/*.test.js", "coverage": "tap --coverage-report=text test/*.test.js" }, "engines": { diff --git a/exp_scenario/permissions.ts b/exp_scenario/permissions.ts index bbaafacd..5fc7aa52 100644 --- a/exp_scenario/permissions.ts +++ b/exp_scenario/permissions.ts @@ -1,4 +1,4 @@ -import * as lib from "@clusterio/lib"; +import type * as lib from "@clusterio/lib"; /** * Permissions checked by the scenario in game through exp_roles. @@ -7,9 +7,9 @@ import * as lib from "@clusterio/lib"; * underscores, see module/commands/_authorities.lua. Everything else is checked * by name at its call site. */ -type Definition = [name: string, title: string, description: string, grantByDefault?: boolean]; +type Definition = readonly [name: Name, title: string, description: string, grantByDefault?: boolean]; -const definitions: Definition[] = [ +const definitions = [ ["exp_scenario.bypass.deconstruction_log", "Deconstruction log bypass", "Be excluded from the deconstruction log."], ["exp_scenario.bypass.entity_protection", "Bypass entity protection", "Remove entities that the protection filter would block."], ["exp_scenario.bypass.nuke_protection", "Bypass nuke protection", "Use nukes without the nuke protection restrictions."], @@ -118,8 +118,12 @@ const definitions: Definition[] = [ ["exp_scenario.player.admin", "Factorio admin", "Be promoted to Factorio admin while holding a role with this permission."], ["exp_scenario.player.instant_respawn", "Instant respawn", "Respawn after two seconds instead of the default delay."], ["exp_scenario.player.spectator", "Spectator", "Remove the zoom to world noise effect, as Factorio does for spectators."], -]; +] as const satisfies readonly Definition[]; -for (const [name, title, description, grantByDefault] of definitions) { - lib.definePermission({ name, title, description, grantByDefault }); -} +/** Name of a permission defined by the scenario, added to lib.Permissions in index.ts. */ +export type ScenarioPermissionName = (typeof definitions)[number][0]; + +export const permissions = definitions.map((definition: Definition): lib.PermissionDefinition => { + const [name, title, description, grantByDefault] = definition; + return { name, title, description, grantByDefault }; +}); diff --git a/exp_scenario/seed.ts b/exp_scenario/seed.ts index 37edbd63..64b34550 100644 --- a/exp_scenario/seed.ts +++ b/exp_scenario/seed.ts @@ -1,3 +1,4 @@ +import type * as lib from "@clusterio/lib"; import { RoleColor } from "@expcluster/roles"; /** @@ -19,7 +20,7 @@ export interface SeedRole { isAdmin?: boolean; /** Name of the role whose permissions are also granted, applied recursively. */ parent?: string; - permissions: string[]; + permissions: lib.PermissionName[]; /** * Name of the seed group holders are placed in by their highest role. * Without one the holders stay in Factorio's Default group. @@ -329,7 +330,7 @@ export const seedGroups: SeedGroup[] = [ /** The permissions a seed role grants, including those of its parents. */ export function flattenSeedPermissions(role: SeedRole, roles = seedRoles) { - const permissions = new Set(); + const permissions = new Set(); const seen = new Set(); let current: SeedRole | undefined = role; while (current && !seen.has(current.name)) { diff --git a/exp_scenario/test/controller.test.js b/exp_scenario/test/controller.test.js index 18ffedc4..d7b96add 100644 --- a/exp_scenario/test/controller.test.js +++ b/exp_scenario/test/controller.test.js @@ -1,18 +1,19 @@ -"use strict"; -const t = require("tap"); -const lib = require("@clusterio/lib"); -const { Controller } = require("@clusterio/controller"); -const { ControllerPlugin } = require("../dist/node/controller"); -const messages = require("../dist/node/messages"); -const { seedRoles, seedGroups } = require("../dist/node/seed"); -const roles = require("@expcluster/roles"); -const groups = require("@expcluster/permission-groups"); -const { ControllerPlugin: RolesPlugin } = require("@expcluster/roles/dist/node/controller"); -const { ControllerPlugin: GroupsPlugin } = require("@expcluster/permission-groups/dist/node/controller"); -const { GroupRecord, GroupPermissions, RoleMappingRecord } = require("@expcluster/permission-groups"); +import t from "tap"; +import * as lib from "@clusterio/lib"; +import { Controller } from "@clusterio/controller"; +import { ControllerPlugin } from "../dist/node/controller.js"; +import * as messages from "../dist/node/messages.js"; +import { seedRoles, seedGroups } from "../dist/node/seed.js"; +import * as roles from "@expcluster/roles"; +import * as groups from "@expcluster/permission-groups"; +import { ControllerPlugin as RolesPlugin } from "@expcluster/roles/dist/node/controller.js"; +import { ControllerPlugin as GroupsPlugin } from "@expcluster/permission-groups/dist/node/controller.js"; +import { GroupRecord, GroupPermissions, RoleMappingRecord } from "@expcluster/permission-groups"; -// Importing this defines the permissions the seed grants -require("../dist/node/permissions"); +import { plugin } from "../dist/node/index.js"; + +// Registering the plugin defines the permissions the seed grants +lib.registerPluginPermissions([plugin]); // The controller validates message classes against the link registry for (const Message of [messages.SeedRequest, ...roles.plugin.messages, ...groups.plugin.messages]) { diff --git a/exp_scenario/test/seed.test.js b/exp_scenario/test/seed.test.js index 87e198a6..c201409d 100644 --- a/exp_scenario/test/seed.test.js +++ b/exp_scenario/test/seed.test.js @@ -1,10 +1,11 @@ -"use strict"; -const t = require("tap"); -const lib = require("@clusterio/lib"); -const { seedRoles, seedGroups, flattenSeedPermissions } = require("../dist/node/seed"); +import t from "tap"; +import * as lib from "@clusterio/lib"; +import { seedRoles, seedGroups, flattenSeedPermissions } from "../dist/node/seed.js"; -// Importing this defines the permissions the seed grants -require("../dist/node/permissions"); +import { plugin } from "../dist/node/index.js"; + +// Registering the plugin defines the permissions the seed grants +lib.registerPluginPermissions([plugin]); t.test("seedRoles[] grant only defined permissions", t2 => { for (const role of seedRoles) { diff --git a/exp_scenario/tsconfig.browser.json b/exp_scenario/tsconfig.browser.json index 1e3889e7..b67577c5 100644 --- a/exp_scenario/tsconfig.browser.json +++ b/exp_scenario/tsconfig.browser.json @@ -1,4 +1,4 @@ { "extends": "../tsconfig.browser.json", - "include": [ "web/**/*.tsx", "web/**/*.ts", "messages.ts", "package.json" ], + "include": [ "web/**/*.tsx", "web/**/*.ts", "index.ts", "messages.ts", "permissions.ts", "package.json" ], } diff --git a/exp_scenario/web/components/Seed.tsx b/exp_scenario/web/components/Seed.tsx index 619d79df..8131b137 100644 --- a/exp_scenario/web/components/Seed.tsx +++ b/exp_scenario/web/components/Seed.tsx @@ -3,7 +3,7 @@ import { Button, Popconfirm } from "antd"; import { ControlContext, SectionHeader, useAccount, notifyErrorHandler } from "@clusterio/web_ui"; -import { SeedRequest } from "../../messages"; +import { SeedRequest } from "../../messages.js"; /** Button on the roles page which creates the roles and permission groups the scenario shipped with. */ export default function Seed() { diff --git a/exp_scenario/webpack.config.js b/exp_scenario/webpack.config.cjs similarity index 100% rename from exp_scenario/webpack.config.js rename to exp_scenario/webpack.config.cjs diff --git a/exp_server_ups/index.ts b/exp_server_ups/index.ts index cdfc8663..404259e9 100644 --- a/exp_server_ups/index.ts +++ b/exp_server_ups/index.ts @@ -12,7 +12,7 @@ export const plugin: lib.PluginDeclaration = { title: "ExpGaming - Server UPS", description: "Clusterio plugin providing in game server ups counter", - instanceEntrypoint: "./dist/node/instance", + instanceEntrypoint: "./dist/node/instance.js", instanceConfigFields: { "exp_server_ups.update_interval": { title: "Update Interval", diff --git a/exp_server_ups/package.json b/exp_server_ups/package.json index 25baa73f..e2a01051 100644 --- a/exp_server_ups/package.json +++ b/exp_server_ups/package.json @@ -5,6 +5,7 @@ "author": "Cooldude2606 ", "license": "MIT", "repository": "explosivegaming/ExpCluster", + "type": "module", "main": "dist/node/index.js", "scripts": { "prepare": "tsc --build && webpack-cli --env production" diff --git a/exp_server_ups/webpack.config.js b/exp_server_ups/webpack.config.cjs similarity index 100% rename from exp_server_ups/webpack.config.js rename to exp_server_ups/webpack.config.cjs diff --git a/exp_util/index.ts b/exp_util/index.ts index c169aefd..12957919 100644 --- a/exp_util/index.ts +++ b/exp_util/index.ts @@ -4,5 +4,5 @@ export const plugin: lib.PluginDeclaration = { name: "exp_util", title: "ExpGaming Module Utilities", description: "Provides extensions and overrides of base Lua library functions, and provides utility modules for improved module compatibly", - instanceEntrypoint: "./dist/node/instance", + instanceEntrypoint: "./dist/node/instance.js", }; diff --git a/exp_util/package.json b/exp_util/package.json index ec10ff5d..3dfa39e2 100644 --- a/exp_util/package.json +++ b/exp_util/package.json @@ -5,6 +5,7 @@ "author": "Cooldude2606 ", "license": "MIT", "repository": "explosivegaming/ExpCluster", + "type": "module", "main": "dist/node/index.js", "scripts": { "prepare": "tsc --build && webpack-cli --env production" diff --git a/exp_util/webpack.config.js b/exp_util/webpack.config.cjs similarity index 100% rename from exp_util/webpack.config.js rename to exp_util/webpack.config.cjs diff --git a/test/common.js b/test/common.js index bf63b030..f6c476f5 100644 --- a/test/common.js +++ b/test/common.js @@ -1,5 +1,4 @@ -"use strict"; -const { compile } = require("@clusterio/lib"); +import { compile } from "@clusterio/lib"; /** * Generate a flat array of tests from a matrix of inputs. @@ -49,7 +48,7 @@ function testRoundTripJsonSerialisable(t, Class, tests) { } } -module.exports = { +export { testMatrix, testRoundTripJsonSerialisable, }; diff --git a/test/lua/runner.js b/test/lua/runner.js index 51019609..14dbb9d0 100644 --- a/test/lua/runner.js +++ b/test/lua/runner.js @@ -1,7 +1,6 @@ -"use strict"; -const path = require("node:path"); -const fs = require("node:fs"); -const { lua, lauxlib, lualib, to_luastring, to_jsstring } = require("fengari"); +import path from "node:path"; +import fs from "node:fs"; +import { createRequire } from "node:module"; /** * Run one lua test file in its own lua state and return its results. @@ -21,6 +20,8 @@ const { lua, lauxlib, lualib, to_luastring, to_jsstring } = require("fengari"); * @returns {{ name: string, ok: boolean, detail?: string }[]} */ function runLuaTests(envFile, testFile) { + // fengari is a devDependency of the plugin whose tests run, so resolve it from there + const { lua, lauxlib, lualib, to_luastring, to_jsstring } = createRequire(testFile)("fengari"); const L = lauxlib.luaL_newstate(); lualib.luaL_openlibs(L); @@ -38,7 +39,7 @@ function runLuaTests(envFile, testFile) { }; load(envFile); - lua.lua_pushstring(L, to_luastring(__dirname)); + lua.lua_pushstring(L, to_luastring(import.meta.dirname)); call(); // The environment stays on the stack and is passed to the test chunk @@ -60,4 +61,4 @@ function reportLuaTests(t, envFile, testFile) { t.end(); } -module.exports = { runLuaTests, reportLuaTests }; +export { runLuaTests, reportLuaTests }; diff --git a/tsconfig.node.json b/tsconfig.node.json index a2e84c8b..b5f49789 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -2,7 +2,7 @@ "extends": "./tsconfig.base.json", "compilerOptions": { "lib": ["es2023"], - "module": "node16", + "module": "nodenext", "moduleResolution": "node16", "target": "es2022", "forceConsistentCasingInFileNames": true,