Skip to main content

stacks-plugins

Use when working with the Stacks preload chain and Bun plugins — the env plugin, the framework preloader, how auto-imports get injected into globalThis, why a command does or does not see framework globals, the bun-plugin-stx static-serve plugin, or writing a Bun plugin. Covers bunfig.toml preload and storage/framework/defaults/resources/plugins/preloader.ts.

来源信息

仓库
stacksjs/bench-review
最近来源活动
2026年7月27日 14:26
检测到的 SKILL.md 语言
英语
星标
3
分支
0

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
stacks-plugins
description
Use when working with the Stacks preload chain and Bun plugins — the env plugin, the framework preloader, how auto-imports get injected into globalThis, why a command does or does not see framework globals, the bun-plugin-stx static-serve plugin, or writing a Bun plugin. Covers bunfig.toml preload and storage/framework/defaults/resources/plugins/preloader.ts.
license
MIT
compatibility
Bun >= 1.3.0, TypeScript
allowed-tools
Read Edit Write Bash Grep Glob
# Stacks Plugins and the Preload Chain Stacks has **no `@stacksjs/plugins` package**. "Plugins" here means Bun's native plugin API plus the two modules `bunfig.toml` preloads before any Stacks code runs. If you are chasing "why is `User` undefined in this command" or "why did `.env` not decrypt", this is the skill. ## Key Paths - Preload declaration: `bunfig.toml` (`preload`, at the root and under `[test]`) - Env plugin: `storage/framework/core/env/plugin.ts` - Framework preloader: `storage/framework/defaults/resources/plugins/preloader.ts` - Static-serve plugin: `bunfig.toml`, `[serve.static] plugins = ["bun-plugin-stx"]` ## The preload chain `bunfig.toml` preloads two modules, **in this order**: ```toml preload = [ "./storage/framework/core/env/plugin.ts", "./storage/framework/defaults/resources/plugins/preloader.ts", ] ``` 1. **Env plugin** loads `.env` and decrypts dotenvx-style encrypted values, so everything downstream reads plaintext from `process.env`. 2. **Preloader** loads env files for the resolved environment, then injects the framework's server auto-imports into `globalThis`. Order is load-bearing: the preloader reads decrypted secrets, so it has to run second. Tests preload a different pair - the env plugin and `tests/setup.ts` - so the suite gets production env semantics without the auto-import graph. ## What the preloader does ### 1. Resolves the environment Before loading any `.env` file it works out `APP_ENV`, because that decides *which* file gets decrypted: - `deploy staging` and `deploy --staging` both resolve to `staging`. Both forms are handled; CI uses the flag form. - `cloud:remove`, `cloud:destroy`, `cloud:cleanup`, `undeploy` force `production`. - Bare `deploy` defaults to `production`. ### 2. Loads and decrypts env files Gated on **`isRepl` / `isPostinstall` only** - never on the fast-command list below. Fast commands (`migrate`, `build`, `seed`, ...) genuinely need decrypted config; gating them here caused stacksjs/stacks#2048, where an encrypted `.env.<env>` silently never decrypted even with the key present. Postinstall skips because `@stacksjs/env` may not be linked yet mid-install. ### 3. Injects auto-imports into `globalThis` Loads ~24 framework packages plus everything in `app/Jobs/` and `resources/functions/`, assigning every named export onto `globalThis`. That is why `await User.find(1)` and `response.json(...)` work in an action with no import statement. See `stacks-auto-imports` for the full manifest. A `protectedGlobals` set guards the runtime: `process`, `fetch`, `Promise`, `Bun`, `console` and friends are never overwritten, whatever a package exports. ## The fast-command skip (the usual gotcha) Auto-import injection is **skipped** when the first CLI argument matches: ``` dev build test lint migrate fresh seed generate make key:generate scaffold:crud version help --version -v --help -h ``` Prefix matches count, so `generate:migrations` skips along with `generate`. Two reasons: cold-start speed, and correctness. Codegen commands must not pull the router and ORM graph in before `bun-query-builder` can diff schemas - a broken `@stacksjs/bun-router` install used to make `generate:migrations` exit 1 with no output at all, because the preloader died loading `@stacksjs/router`. **Consequence:** inside those commands, framework globals do not exist. Import what you need explicitly. The REPL skips too (`!process.argv[1]`), as does anything running under `npm_lifecycle_event === 'postinstall'`. ### Opting back in The preloader exports `loadAutoImports()` so a server entrypoint can pull the graph in deliberately: ```ts const { loadAutoImports } = await import('../../../defaults/resources/plugins/preloader.ts') await loadAutoImports() ``` `dev/api.ts` does exactly this. Bun consumes `--watch`, so a directly-run server script sees an empty `argv.slice(2)` and would otherwise be indistinguishable from a bare invocation. ## Writing a Bun plugin Use Bun's native API directly - there is no framework wrapper to import: ```ts import { plugin } from 'bun' plugin({ name: 'my-loader', setup(build) { build.onLoad({ filter: /\.custom$/ }, async (args) => { const contents = await Bun.file(args.path).text() return { contents: transform(contents), loader: 'ts' } }) }, }) ``` Register it by adding the module to `preload` in `bunfig.toml`. A plugin only affects the process that preloaded it. ## Gotchas - **No `@stacksjs/plugins` package.** Import `plugin` from `bun`. - **Preload order matters.** Env decryption has to precede the preloader. - **Fast commands have no globals.** If a `make:*` or `generate:*` command needs a framework symbol, import it - do not rely on auto-imports. - **`bun-plugin-stx` is a static-serve plugin**, wired through `[serve.static] plugins` in `bunfig.toml`. The preloader's own `import 'bun-plugin-stx'` line is commented out; stx compilation in the dev server runs through the stx package, not that import. - **Preloader failures are silent per package.** Each import sits in its own `try {} catch {}`, so a package that fails to build leaves its globals undefined instead of crashing the process. A mysteriously undefined global is usually an unbuilt package - check `storage/framework/core/<pkg>/dist`. - **Adding a global?** Export it from a package already in the preloader's list, or from `resources/functions/`, then run `buddy generate --types` so the declarations match the runtime.
在 GitHub 查看