ESLint plugin for TitanPL projects. Enforces TitanPL-specific rules including blocking Node.js built-in modules, prohibiting async/await, and ensuring correct drift() usage. Automatically detects async methods from .d.ts files.
npm install eslint-plugin-titanplESLint plugin for Titan Planet projects. Enforces TitanPL-specific rules including blocking Node.js built-in modules, prohibiting async/await, and ensuring correct drift() usage.
``bash`
npm install eslint-plugin-titanpl --save-dev
`javascript
// eslint.config.js
import { titanpl } from 'eslint-plugin-titanpl';
export default [
titanpl,
// ...other configs
];
`
`javascript
// eslint.config.js
import plugin from 'eslint-plugin-titanpl';
import globals from 'globals';
export default [
{
files: ['app/*/.js'],
plugins: {
titanpl: plugin
},
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
globals: {
...globals.es2024,
t: 'readonly',
Titan: 'readonly',
drift: 'readonly'
}
},
rules: {
'no-undef': 'error',
'titanpl/no-node-builtins': 'error',
'titanpl/no-async-await': 'error',
'titanpl/drift-only-titan-async': 'error',
'titanpl/require-drift': 'error'
}
}
];
`
---
Disallows importing Node.js built-in modules that are not available in the TitanPL runtime.
#### ❌ Incorrect
`javascript`
import fs from 'fs';
import { readFile } from 'node:fs';
import path from 'path';
#### ✅ Correct
`javascript`
const content = drift(t.core.fs.readFile('config.json'));
const fullPath = t.core.path.join('dir', 'file.txt');
---
Disallows the use of async, await, .then(), .catch(), and .finally() in TitanPL. TitanPL uses drift() for async operations instead.
#### ❌ Incorrect
`javascript
async function fetchData() {
const data = await fetch('/api');
return data;
}
promise.then(callback).catch(errorHandler);
`
#### ✅ Correct
`javascript`
function fetchData() {
const data = drift(t.fetch('/api'));
return data;
}
---
Ensures drift() is only used with async TitanPL native methods.
1. The argument must be a function call (not a reference or literal)
2. The method must be from t or Titan (including all alias types).d.ts
3. The method must be asynchronous (detected automatically from files)
#### ❌ Incorrect
`javascript
// Non-Titan methods
drift(myFunction());
drift(console.log('test'));
drift(Math.random());
// Sync Titan methods (don't need drift)
drift(t.core.path.join('a', 'b'));
drift(t.core.url.parse('http://...'));
drift(t.core.crypto.uuid());
drift(t.core.os.platform());
// Sync aliases (don't need drift)
const { join } = t.core.path;
drift(join('a', 'b'));
// Sync module alias sub-methods (don't need drift)
const myPath = t.core.path;
drift(myPath.join('a', 'b'));
const db = t.db;
drift(db.isConnected());
// Method references without calling them
drift(t.fetch); // Missing ()
drift(readFile); // Missing ()
drift(db.query); // Missing ()
// Literals and expressions
drift('string');
drift(123);
drift(() => {});
drift(); // No argument
`
#### ✅ Correct
`javascript
// Direct async Titan methods
drift(t.fetch('/api'));
drift(t.core.fs.readFile('/file'));
drift(t.core.crypto.hash('data'));
drift(t.core.time.sleep(1000));
// Titan namespace
drift(Titan.fetch('/api'));
drift(Titan.core.fs.readFile('/file'));
// Destructured aliases
const { fetch } = t;
drift(fetch('/api'));
const { readFile } = t.core.fs;
drift(readFile('/file'));
// Declare global aliases
// declare global { const globalFetch: typeof t.fetch }
drift(globalFetch('/api'));
// Exported aliases
// export const exportedFetch = t.fetch;
drift(exportedFetch('/api'));
// Simple assignment aliases
const myFetch = t.fetch;
drift(myFetch('/api'));
// Module assignment aliases
const db = t.db;
drift(db.query('SELECT 1'));
const fs = t.core.fs;
drift(fs.readFile('/file'));
// Object property aliases
const utils = { fetch: t.fetch, read: t.core.fs.readFile };
drift(utils.fetch('/api'));
drift(utils.read('/file'));
// Third-party async methods
drift(t.ws.connect('wss://example.com'));
drift(t.cache.get('key'));
`
---
Requires drift() wrapper for async TitanPL native methods. Complements drift-only-titan-async by catching unwrapped async calls.
#### ❌ Incorrect
`javascript
// Direct async without drift
t.fetch('/api/data');
t.core.fs.readFile('/file');
t.core.crypto.hash('data');
// Destructured aliases without drift
const { fetch } = t;
fetch('/api/data');
const { readFile } = t.core.fs;
readFile('/file');
// Simple assignment aliases without drift
const myFetch = t.fetch;
myFetch('/api');
// Module assignment aliases without drift
const db = t.db;
db.query('SELECT 1');
const fs = t.core.fs;
fs.readFile('/file');
// Object property aliases without drift
const helpers = { query: t.db.query };
helpers.query('SELECT 1');
// Third-party async without drift
t.ws.connect('wss://example.com');
t.cache.get('key');
`
#### ✅ Correct
`javascript
// Async methods with drift
drift(t.fetch('/api/data'));
drift(t.core.fs.readFile('/file'));
drift(t.core.crypto.hash('data'));
// Sync methods don't need drift
t.core.path.join('a', 'b');
t.core.crypto.uuid();
t.log('hello');
// All alias types with drift
const { fetch } = t;
drift(fetch('/api/data'));
const myFetch = t.fetch;
drift(myFetch('/api'));
const db = t.db;
drift(db.query('SELECT 1'));
const helpers = { query: t.db.query };
drift(helpers.query('SELECT 1'));
// Sync aliases don't need drift
const { join } = t.core.path;
join('a', 'b');
const myPath = t.core.path;
myPath.join('a', 'b');
const db = t.db;
db.isConnected();
`
---
The plugin automatically detects whether a Titan method is async or sync by reading .d.ts files and scanning your project. No configuration required.
``
1. DTS File Reader → Scans node_modules for .d.ts files
Scans project recursively for .d.ts files
Finds "declare namespace t" or "declare namespace Titan"
Extracts methods that return Promise
↓
2. Alias Detection → Detects 6 alias patterns:
• Destructuring: const { fetch } = t
• Declare global: declare global { const fetch: typeof t.fetch }
• Exports: export const fetch = t.fetch
• Simple assignment: const myFetch = t.fetch
• Module assignment: const db = t.db → db.query()
• Object property: const u = { fetch: t.fetch } → u.fetch()
↓ fallback
3. Permissive Fallback → Unknown methods are treated as sync
When you run ESLint, the plugin automatically:
1. Scans node_modules/ for packages with .d.ts files.d.ts
2. Scans your project recursively for and source files (two-pass: type definitions first, then source files)declare namespace t
3. Looks for or declare namespace Titan declarationsPromise<...>
4. Extracts methods and checks if they return
5. Detects aliases from all 6 supported patterns
6. Caches the results for performance
`typescript`
// node_modules/titan-websocket/index.d.ts
declare namespace t {
namespace ws {
function connect(url: string): Promise
function isConnected(): boolean; // Detected as sync
}
}
The plugin will automatically detect t.ws.connect as async and t.ws.isConnected as sync.
---
The plugin detects when Titan methods are aliased and tracks them correctly. This works for six patterns:
`javascript
const { fetch } = t;
const { readFile, writeFile } = t.core.fs;
const { join: pathJoin } = t.core.path;
drift(fetch('/api')); // ✅ fetch is alias of async t.fetch
drift(readFile('/file')); // ✅ readFile is alias of async t.core.fs.readFile
pathJoin('a', 'b'); // ✅ pathJoin is alias of sync t.core.path.join (no drift needed)
drift(pathJoin('a', 'b')); // ❌ Error: sync method doesn't need drift
`
`typescript`
// types/globals.d.ts
declare global {
function fetch(url: string): Promise
const myFetch: typeof t.fetch;
}
`javascript`
drift(fetch('/api')); // ✅ Global fetch detected as async
drift(myFetch('/api')); // ✅ myFetch is alias of async t.fetch
`typescript`
// utils/titan-helpers.ts
export const fetch = t.fetch;
export const readFile = t.core.fs.readFile;
export const pathJoin = t.core.path.join;
`javascript`
drift(fetch('/api')); // ✅ fetch is alias of async t.fetch
drift(readFile('/file')); // ✅ readFile is alias of async t.core.fs.readFile
pathJoin('a', 'b'); // ✅ pathJoin is alias of sync method (no drift needed)
`javascript
const myFetch = t.fetch;
const myHash = t.core.crypto.hash;
drift(myFetch('/api')); // ✅ myFetch is alias of async t.fetch
drift(myHash('data')); // ✅ myHash is alias of async t.core.crypto.hash
`
Assigns an entire Titan module to a variable. The plugin resolves sub-method calls on that variable.
`javascript
const db = t.db;
const fs = t.core.fs;
const myPath = t.core.path;
drift(db.query('SELECT 1')); // ✅ db.query → t.db.query (async)
drift(fs.readFile('/file')); // ✅ fs.readFile → t.core.fs.readFile (async)
myPath.join('a', 'b'); // ✅ myPath.join → t.core.path.join (sync, no drift needed)
db.isConnected(); // ✅ db.isConnected → t.db.isConnected (sync, no drift needed)
db.query('SELECT 1'); // ❌ Error: async method without drift
drift(myPath.join('a', 'b')); // ❌ Error: sync method doesn't need drift
`
Assigns individual Titan methods as properties of a plain object.
`javascript
const titanUtils = { fetch: t.fetch, read: t.core.fs.readFile };
const dbHelpers = { query: t.db.query, exec: t.db.execute };
drift(titanUtils.fetch('/api')); // ✅ titanUtils.fetch → t.fetch (async)
drift(dbHelpers.query('SELECT 1'));// ✅ dbHelpers.query → t.db.query (async)
titanUtils.fetch('/api'); // ❌ Error: async method without drift
`
---
The plugin scans your entire project for .d.ts files and source files to detect Titan types and aliases.
| Source | What's Scanned |
|--------|----------------|
| node_modules/ | Packages with .d.ts files containing Titan declarations |.d.ts
| Project root | All files recursively (scanned first for type definitions) |.js
| Source files | , .ts, .mjs files for alias patterns (scanned second) |
The plugin uses a two-pass scan to ensure correct detection:
1. First pass: All .d.ts files are parsed to build the complete method registry.js
2. Second pass: Source files (, .ts, .mjs) are parsed for alias patterns
This order guarantees that when the plugin encounters const fs = t.core.fs in a source file, it already knows all methods under t.core.fs and can correctly mark fs as a module alias.
The following directories are automatically skipped:
- node_modules/ (scanned separately for packages).git/
- , .svn/dist/
- , build/coverage/
- .next/
- , .nuxt/, .output/vendor/
- .cache/
-
You can define types anywhere in your project. Common locations:
- types/titan.d.tssrc/types/titan.d.ts
- typings/titan.d.ts
- titan.d.ts
- .d.ts
- Any file in your project
If a method is not found in any .d.ts file, it's treated as sync (permissive fallback). This means:
- No false positives for libraries without type definitions
- Add .d.ts files to enable accurate detection
---
Third-party Titan libraries should include a .d.ts file to enable automatic async detection.
Create a .d.ts file and reference it in package.json:
`typescript`
// index.d.ts
declare namespace t {
namespace ws {
function connect(url: string): Promise
function send(data: string): Promise
function close(): Promise
function isConnected(): boolean;
}
}
`json`
// package.json
{
"name": "titan-websocket",
"types": "./index.d.ts"
}
Just install the library. The plugin detects async methods automatically:
`bash`
npm install titan-websocket
`javascript
// ✅ Plugin knows t.ws.connect is async
drift(t.ws.connect('ws://example.com'));
// ✅ Plugin knows t.ws.isConnected is sync
const connected = t.ws.isConnected();
// ✅ Works with all alias types
const { connect, isConnected } = t.ws;
drift(connect('ws://example.com'));
const connected = isConnected();
const ws = t.ws;
drift(ws.connect('ws://example.com'));
ws.isConnected();
`
---
Recommended configuration with all rules enabled:
`javascript
import { titanpl } from 'eslint-plugin-titanpl';
export default [titanpl];
`
Rules enabled:
- no-node-builtins: errorno-async-await
- : errordrift-only-titan-async
- : errorrequire-drift
- : error
---
| Module | Methods |
|--------|---------|
| t.fetch / Titan.fetch | HTTP requests |t.core.fs
| | readFile, writeFile, remove, mkdir, readdir, stat, exists |t.core.crypto
| | hash, encrypt, decrypt, hashKeyed |t.core.net
| | resolveDNS |t.core.time
| | sleep |t.core.session
| | get, set, delete, clear |
| Module | Methods |
|--------|---------|
| t.core.path | join, resolve, dirname, basename, extname |t.core.url
| | parse, format, SearchParams |t.core.crypto
| | uuid, randomBytes, compare |t.core.os
| | platform, cpus, totalMemory, freeMemory, tmpdir |t.core.buffer
| | fromBase64, toBase64, fromHex, toHex, fromUtf8, toUtf8 |t.core.time
| | now, timestamp |t.core.proc
| | pid, uptime |t.core.net
| | ip |t.core.ls
| | get, set, remove, clear, keys |t.core.cookies
| | get, set, delete |t.log
| | Logging |
> Note: These tables show the core Titan methods. Third-party libraries can extend t or Titan with additional methods. The plugin detects them automatically from .d.ts files.
---
`
// no-node-builtins
"fs" is not available in TitanPL. Use t.core.fs instead.
// no-async-await
async functions are not allowed in TitanPL. Use drift() for async operations.
.then() is not allowed in TitanPL. Use drift() for async operations.
// drift-only-titan-async
drift() should only be used with Titan (t. or Titan.) async method calls.
drift() should not be used with sync Titan method "t.core.path.join". Remove the drift() wrapper.
drift() should not be used with sync Titan method "pathJoin". Remove the drift() wrapper.
drift() requires a function call as argument. Use drift(method(...)) instead of drift(method).
drift() requires an argument.
// require-drift
Async Titan method "t.fetch" must be wrapped in drift(). Use drift(t.fetch(...)) instead.
Async Titan method "fetch" must be wrapped in drift(). Use drift(fetch(...)) instead.
`
---
Titan Planet compiles your JavaScript/TypeScript into a native binary with an embedded V8 runtime. Unlike Node.js:
- No Node.js Event Loop — Request/Response model
- No require() — Use ES modules or bundled dependencies
- No async/await — Use drift() for async operations
- True Isolation — Each request is isolated
- Native Performance — Rust + V8 combination
This plugin helps catch incompatible code at lint time rather than runtime.
---
- Six alias patterns: Full support for destructuring, declare global, exports, simple assignment, module assignment, and object property aliases
- Module alias resolution: const db = t.db; db.query() correctly resolves to t.db.queryconst u = { fetch: t.fetch }; u.fetch()
- Object property aliases: correctly resolves.d.ts
- Two-pass scanning: Type definitions () are loaded before source files to ensure correct module alias detectioncheckForAlias("fs.readFile")
- Compound path resolution: correctly resolves through module aliases (fs → t.core.fs → t.core.fs.readFile)
- Project scanning: Now scans entire project recursively for .d.ts filesconst { fetch } = t
- Alias detection: Detects destructuring ()declare global { const fetch: typeof t.fetch }
- Declare global support: Detects export const fetch = t.fetch
- Export detection: Detects isTitanCallee()
- Improved reliability: Better null handling in
- Initial DTS file checker for automatic async method detection
- Scans node_modules/` for Titan type definitions
---
ISC © David200197