API Specstable
7 min read time

Documentation

import/export

Share code between scripts using ES6-style named imports and exports.

apimodulesimportexport

Importing and Exporting

PixelScript uses ES6-style module imports and exports to share code between scripts.
Use exports to make functions, classes, and values available to other scripts, and imports to use them.

Note: Default exports are not supported. All exports must be named exports.

Exporting from Scripts

Export functions with export function

Make functions available to other scripts by prefixing them with export.

// utils/messages.js export function success(message) { return `§a✓ §f${message}`; } export function error(message) { return `§c✗ §f${message}`; } export function info(message) { return `§e§f${message}`; }

Export constants with export const

Export values and constants using export const.

// utils/constants.js export const SPAWN_WORLD = "world"; export const SPAWN_X = 0; export const SPAWN_Y = 64; export const SPAWN_Z = 0; export const MAX_PLAYERS = 100; export const SERVER_NAME = "PSCraft";

Export classes with export class

// utils/economy.js export class Economy { constructor() { this.accounts = new Map(); } getBalance(playerUUID) { return this.accounts.get(playerUUID) || 0; } setBalance(playerUUID, amount) { this.accounts.set(playerUUID, amount); } addMoney(playerUUID, amount) { const current = this.getBalance(playerUUID); this.setBalance(playerUUID, current + amount); } }

Importing Scripts

Import with import { name } from "path"

Load exported values from another script using import.

// features/welcome.js import { success, info } from '../utils/messages.js'; registerListener($.PlayerJoinEvent, (event) => { const player = event.getPlayer(); player.sendMessage(success("Welcome to the server!")); player.sendMessage(info("Type /help to get started")); });

Import multiple items

// features/teleport.js import { SPAWN_WORLD, SPAWN_X, SPAWN_Y, SPAWN_Z } from '../utils/constants.js'; function teleportToSpawn(player) { const location = newLocation(SPAWN_WORLD, SPAWN_X, SPAWN_Y, SPAWN_Z); player.teleport(location); }

Import classes

// features/shop.js import { Economy } from '../utils/economy.js'; const economy = new Economy(); registerCommand('balance', (sender) => { const uuid = sender.getUniqueId().toString(); const balance = economy.getBalance(uuid); sender.sendMessage(`§eYour balance: §a$${balance}`); });

Import Paths

Paths in import statements are relative to the current script file:

// scripts/features/economy/shop.js import { formatCurrency } from './currency.js'; // Same directory import { registerWarp } from '../warps/commands.js'; // Parent's sibling directory import { success, error } from '../../utils/messages. js'; // Two levels up, then down

All import paths must include the .js file extension.

Script Lifecycle with Imports

Imported scripts have special reload behavior:

Single Instance:

  • An imported script only has one instance at runtime
  • Multiple scripts importing the same module share that single instance

Reload Cascade:

  • When an imported script is modified, it reloads
  • All scripts that import it also reload
  • Their children reload as well, creating a reload cascade

Shared Dependencies:

  • If two scripts import the same utility, they share it
  • Changes to that utility affect all importers simultaneously
// utils/database.js - Imported by many scripts let connectionPool = null; export function getConnection() { if (!connectionPool) { connectionPool = createPool(); } return connectionPool; } // This instance is shared by all scripts that import it // If database.js changes, ALL scripts importing it will reload

Practical Examples

Shared utility library

// utils/player_utils.js export function isAdmin(player) { return player.hasPermission('server.admin'); } export function isModerator(player) { return player.hasPermission('server.moderator'); } export function hasVIP(player) { return player.hasPermission('server.vip'); } export function sendActionBar(player, message) { player.sendActionBar(message); }
// features/admin_commands.js import { isAdmin } from '../utils/player_utils. js'; registerCommand('announce', (sender, args) => { if (!isAdmin(sender)) { sender.sendMessage("§cYou don't have permission!"); return; } const message = args. join(' '); Bukkit.broadcastMessage(`§6[ADMIN] §f${message}`); });

Database wrapper

// utils/database.js export function savePlayerData(uuid, data, callback) { Scheduler.runAsync(() => { Sql.makeUpdate( "UPDATE player_data SET data = ? WHERE uuid = ?", (stmt) => { stmt. setString(1, JSON.stringify(data)); stmt.setString(2, uuid); }, callback ); }); } export function loadPlayerData(uuid, callback) { Scheduler.runAsync(() => { Sql.makeQuery( "SELECT data FROM player_data WHERE uuid = ?", (stmt) => stmt.setString(1, uuid), (rs) => { if (rs !== null && rs.next()) { const data = JSON.parse(rs.getString("data")); Scheduler.runSync(() => callback(data)); } else { Scheduler.runSync(() => callback(null)); } } ); }); }
// features/player_stats.js import { loadPlayerData } from '../utils/database.js'; registerListener($.PlayerJoinEvent, (event) => { const player = event.getPlayer(); const uuid = player.getUniqueId().toString(); loadPlayerData(uuid, (data) => { if (data) { player.sendMessage(`Welcome back! Level: ${data.level}`); } else { player. sendMessage("Welcome new player!"); } }); });

Configuration manager

// utils/config.js const config = { spawn: { world: "world", x: 0, y: 64, z: 0 }, messages: { welcome: "§eWelcome to PSCraft! ", goodbye: "§eSee you soon!" }, features: { pvp: false, economy: true, warps: true } }; export function get(path) { const parts = path.split('.'); let value = config; for (const part of parts) { value = value[part]; if (value === undefined) { return null; } } return value; } export { config };
// features/spawn. js import { get } from '../utils/config.js'; registerCommand('spawn', (sender) => { const spawnConfig = get('spawn'); const location = newLocation( spawnConfig.world, spawnConfig.x, spawnConfig.y, spawnConfig.z ); sender.teleport(location); sender.sendMessage(get('messages.welcome')); });

Singleton pattern

// utils/economy.js class EconomyManager { constructor() { this.accounts = new Map(); } getBalance(playerUUID) { return this.accounts. get(playerUUID) || 0; } setBalance(playerUUID, amount) { this.accounts.set(playerUUID, amount); } addMoney(playerUUID, amount) { const current = this.getBalance(playerUUID); this.setBalance(playerUUID, current + amount); } removeMoney(playerUUID, amount) { const current = this.getBalance(playerUUID); if (current >= amount) { this.setBalance(playerUUID, current - amount); return true; } return false; } } // Export a singleton instance export const economy = new EconomyManager();
// features/shop.js import { economy } from '../utils/economy.js'; registerCommand('balance', (sender) => { const uuid = sender. getUniqueId().toString(); const balance = economy.getBalance(uuid); sender.sendMessage(`§eYour balance: §a$${balance}`); }); registerCommand('pay', (sender, args) => { if (args.length < 2) { sender.sendMessage("§cUsage: /pay <player> <amount>"); return; } const target = Bukkit.getPlayer(args[0]); const amount = parseInt(args[1]); if (!target) { sender.sendMessage("§cPlayer not found"); return; } const senderUUID = sender.getUniqueId().toString(); const targetUUID = target.getUniqueId().toString(); if (economy.removeMoney(senderUUID, amount)) { economy.addMoney(targetUUID, amount); sender.sendMessage(`§aYou paid §e$${amount} §ato ${target.getName()}`); target.sendMessage(`§aYou received §e$${amount} §afrom ${sender.getName()}`); } else { sender.sendMessage("§cInsufficient funds"); } });

Type-like interfaces with constants

// utils/types.js export const PlayerRank = { MEMBER: 'member', VIP: 'vip', MODERATOR: 'moderator', ADMIN: 'admin' }; export const GameState = { WAITING: 'waiting', STARTING: 'starting', ACTIVE: 'active', ENDING: 'ending' }; export const MessageType = { SUCCESS: 'success', ERROR: 'error', INFO: 'info', WARNING: 'warning' };
// features/minigame.js import { GameState } from '../utils/types.js'; let currentState = GameState.WAITING; function startGame() { if (currentState !== GameState.WAITING) { return; } currentState = GameState. STARTING; // Start countdown... }

Avoiding Reload Cascades

Be mindful of import dependencies to avoid unnecessary reloads:

// BAD: Utility imported by 50 scripts // utils/common.js - Changes here reload 50 scripts! export function utilityA() { } export function utilityB() { } export function utilityC() { } // ... 100 more utilities // GOOD: Split into focused modules // utils/messages.js - Only reloads scripts using messages // utils/permissions.js - Only reloads scripts using permissions // utils/formatting.js - Only reloads scripts using formatting

Best Practices

DO:

  • Use named exports for all exports
  • Group related utilities into focused modules
  • Keep imported modules small and focused
  • Use relative paths with .js extension
  • Export singleton instances for shared state
  • Import only what you need

DON'T:

  • Use default exports (not supported)
  • Create circular dependencies (A imports B, B imports A)
  • Put everything in one giant utility file
  • Import modules inside frequently-called functions
  • Forget that changes cascade to all importers
  • Omit the .js file extension

Watched vs Imported Scripts

Understanding the difference helps organize your codebase:

Watched scripts:

  • Loaded via Watcher. watch()
  • Act as reload barriers
  • Don't reload when parent reloads
  • Good for features and subsystems

Imported scripts:

  • Loaded via import statements
  • Reload cascades to importers
  • Good for utilities and shared code
// main.js // Watched: Won't reload when main.js reloads Watcher.watch('features/economy/init.js'); // Imported: Will cause main.js to reload if it changes import { SERVER_NAME } from './utils/config.js';

Import Resolution

PixelScript resolves imports relative to the current file:

// scripts/features/economy/shop.js import { formatCurrency } from './utils. js'; // Resolves to: scripts/features/economy/utils.js import { success } from '../../utils/messages.js'; // Resolves to: scripts/utils/messages.js import { SPAWN_X } from '../../../config.js'; // Resolves to: scripts/config.js

Always use relative paths starting with ./ or ../ and include the .js extension.

Documentation in early stages. Not meant for public consumption.