@ adonisjs/ 组装器
导言
阿多尼斯·贾克 Assembler是AdonisJS用于执行类似任务的一个开发工具包 在监视模式下启动 dev 服务器, (中文(简体) ), 在监视模式下运行测试,以及 应用代码 以修改源文件.
Assembler应始终作为发展依赖而安装. 如果您的工程在生产中需要Assembler API, 您必须重新考虑您的方法 .
目标
Assembler围绕以下目标而建.
- 显示编码接口而不是用户界面。 也就是说,Assembler永远不会暴露任何CLI命令.
- 在单个 API 下封装任务 。 Assembler不提供十种不同的公用设备来运行一款dev服务器,而是会曝光一个API来运行dev服务器.
- 拥有AdonisJS所需的所有开发API. 因此,Assembler号的范围可能会随着时间的推移而扩大.
Dev 服务器
开发服务器可以使用 DevServer 课。 它会运行 bin/server.ts 从AdonisJS项目中产生的文件作为一个孩子进程,并监测其变化(无论是HMR模式还是监视模式).
每当有文件更改 DevServer 将执行文件监视器钩钩,如果需要,将重新启动开发服务器.
您可以导入并使用 DevServer 如下所示.
import ts from 'typescript'
import { DevServer } from '@adonisjs/assembler'
const appRoot = new URL('./', import.meta.url)
const devServer = new DevServer(appRoot, {
/**
* Arguments to pass to the "bin/server.ts" file
*/
scriptArgs: [],
/**
* Arguments to pass to the Node.js CLI
*/
nodeArgs: [],
/**
* An array of metaFiles to watch and re-start the
* HTTP server only if the "reloadServer" flag is
* true.
*/
metaFiles: [
{
pattern: 'resources/views/**/*.edge',
reloadServer: false,
}
]
})
devServer.onError((error) => {
process.exitCode = 1
})
devServer.onClose((exitCode) => {
process.exitCode = exitCode
})
await devServer.runAndWatch(ts)
您可以通过设置启动 HMR 模式的 dev 服务器 hmr: true 并呼唤 start 方法.
const devServer = new DevServer(appRoot, {
hmr: true,
// ...rest of the config
})
await devServer.start()
测试运行器
这个 TestRunner 用于执行 bin/test.ts 您的 AdonisJS 应用程序的文档。 喜欢 DevServer,则 TestRunner 允许您查看文件更改并重新运行测试。 现采取以下步骤,以表模式再进行测试.
- 如果更改的文件是测试文件,则将只重新运行该文件的测试.
- 否则,所有测试将针对运行时应用的初始过滤器重新运行
node ace test命令.
使用量
您可以导入并使用 TestRunner 如下所示.
import ts from 'typescript'
import { TestRunner } from '@adonisjs/assembler'
const appRoot = new URL('./', import.meta.url)
const runner = new TestRunner(appRoot, {
/**
* Arguments to pass to the "bin/test.ts" file
*/
scriptArgs: [],
/**
* Arguments to pass to the Node.js CLI
*/
nodeArgs: [],
/**
* An array of suites and their glob patterns
*/
suites: [
{
name: 'unit',
files: ['tests/unit/**/*.spec.ts']
},
{
name: 'functional',
files: ['tests/functional/**/*.spec.ts']
}
],
/**
* Initial set of filters to apply. These filters
* will be re-applied when re-running tests in
* watch mode
*/
filters: {
suites: ['unit'],
tags: ['@slow']
}
})
await runner.runAndWatch(ts)
你可以运行测试 没有监视器使用 run 方法.
await runner.run()
邦德勒语Name
这个 Bundler 用于创建 AdonisJS 应用程序的生产建筑。 现采取以下步骤生成构件.
- 清理现有建设目录.
- 使用 JavaScript 构建
tsc(TypeScript的官方编译器). - 复制
ace.js创建文件夹。 由于王牌文件以.js扩展名,它不是由 TypeScript 编译器编译的. - 复制
package.json编辑 软件包管理器的锁定文件 您正在使用到build文件夹。 此操作仅支持bun | npm | yarn | pnpm。对于其他捆绑者,您需要手动复制锁文件. - 终也.
使用量
您可以导入并使用 Bundler 如下所示.
import ts from 'typescript'
import { Bundler } from '@adonisjs/assembler'
const appRoot = new URL('./', import.meta.url)
const bundler = new Bundler(appRoot, ts, {
/**
* Metafiles to copy to the build folder
*/
metaFiles: [
{
pattern: 'resources/views/**/*.edge',
reloadServer: false,
}
],
})
代码
Assembler还输出了某些代码来修改一个AdonisJS项目的源文件来配置软件包.
代码mods依赖于AdonisJS的默认,如果一个项目不遵循默认,则将不起作用. 这是一个有意的限制,因为我们只有有限的时间来制作与每一个可能的设置相配合的代码.
使用量
您可以导入并使用 Codemods 如下所示.
import { CodeTransformer } from '@adonisjs/assembler/code_transformer'
const appRoot = new URL('./', import.meta.url)
const transformer = new CodeTransformer(appRoot)
定义输入
定义环境变量的验证规则。 该方法接受一对按键值变量. 这个 key 是嵌入变量名称,并且 value 是作为字符串的验证表达式.
[大 此编码器预计
start/env.ts文件存在且必须拥有export default await Env.create方法调用.另外,代码mod不覆盖给定环境变量的现有验证规则. 这样做是为了尊重应用中的修改.
const transformer = new CodeTransformer(appRoot)
try {
await transformer.defineEnvValidations({
leadingComment: 'App environment variables',
variables: {
PORT: 'Env.schema.number()',
HOST: 'Env.schema.string()',
}
})
} catch (error) {
console.error('Unable to define env validations')
console.error(error)
}
产出
import { Env } from '@adonisjs/core/env'
export default await Env.create(new URL('../', import.meta.url), {
PORT: Env.schema.number(),
HOST: Env.schema.string(),
})
添加Middleware 到Stack 软件
将 AdonisJS 中间软件注册到已知的中间软件堆栈中 。 这种方法接受中间软件堆栈和一系列的中间软件来注册.
中间软件堆是其中之一 server | router | named。 。 。 .
[大 此编码器预计
start/kernel.ts文件要存在,并且必须有一个函数调用中件堆栈,您正试图为此注册中件.
const transformer = new CodeTransformer(appRoot)
try {
await transformer.addMiddlewareToStack('router', [
{
path: '@adonisjs/core/bodyparser_middleware'
}
])
} catch (error) {
console.error('Unable to register middleware')
console.error(error)
}
产出
import router from '@adonisjs/core/services/router'
router.use([
() => import('@adonisjs/core/bodyparser_middleware')
])
您可以定义名为中间软件的如下 .
const transformer = new CodeTransformer(appRoot)
try {
await transformer.addMiddlewareToStack('named', [
{
name: 'auth',
path: '@adonisjs/auth/auth_middleware'
}
])
} catch (error) {
console.error('Unable to register middleware')
console.error(error)
}
更新Rc文件
登记册 providers, (中文(简体) ), commands,定义 metaFiles 财务报告和审定财务报表 commandAliases 页:1 adonisrc.ts 文档.
[大 此编码器预计
adonisrc.ts要存在的文件, 必须有一个export default defineConfig函数调用.
const transformer = new CodeTransformer(appRoot)
try {
await transformer.updateRcFile((rcFile) => {
rcFile
.addProvider('@adonisjs/lucid/db_provider')
.addCommand('@adonisjs/lucid/commands'),
.setCommandAlias('migrate', 'migration:run')
})
} catch (error) {
console.error('Unable to update adonisrc.ts file')
console.error(error)
}
产出
import { defineConfig } from '@adonisjs/core/app'
export default defineConfig({
commands: [
() => import('@adonisjs/lucid/commands')
],
providers: [
() => import('@adonisjs/lucid/db_provider')
],
commandAliases: {
migrate: 'migration:run'
}
})
添加贾帕普吕金
将 Japa 插件注册到 tests/bootstrap.ts 文档.
[大 此编码器预计
tests/bootstrap.ts文件存在且必须拥有export const plugins: Config['plugins']输出.
const transformer = new CodeTransformer(appRoot)
const imports = [
{
isNamed: false,
module: '@adonisjs/core/services/app',
identifier: 'app'
},
{
isNamed: true,
module: '@adonisjs/session/plugins/api_client',
identifier: 'sessionApiClient'
}
]
const pluginUsage = 'sessionApiClient(app)'
try {
await transformer.addJapaPlugin(pluginUsage, imports)
} catch (error) {
console.error('Unable to register japa plugin')
console.error(error)
}
产出
import app from '@adonisjs/core/services/app'
import { sessionApiClient } from '@adonisjs/session/plugins/api_client'
export const plugins: Config['plugins'] = [
sessionApiClient(app)
]
添加维特普卢金
将 Vite 插件注册到 vite.config.ts 文档.
[大 此编码器预计
vite.config.ts文件存在且必须拥有export default defineConfig函数调用.
const transformer = new CodeTransformer(appRoot)
const imports = [
{
isNamed: false,
module: '@vitejs/plugin-vue',
identifier: 'vue'
},
]
const pluginUsage = 'vue({ jsx: true })'
try {
await transformer.addVitePlugin(pluginUsage, imports)
} catch (error) {
console.error('Unable to register vite plugin')
console.error(error)
}
产出
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue({ jsx: true })
]
})
添加政策
将 AdonisJS 保镖政策登记为 policies 从导出的对象 app/policies/main.ts 文档.
[大 此编码器预计
app/policies/main.ts文件为存在,必须导出policies对象.
const transformer = new CodeTransformer(appRoot)
try {
await transformer.addPolicies([
{
name: 'PostPolicy',
path: '#policies/post_policy'
}
])
} catch (error) {
console.error('Unable to register policy')
console.error(error)
}
产出
export const policies = {
UserPolicy: () => import('#policies/post_policy')
}
添加变量
创建一个新的验证符文件,或在已有文件中添加验证符 。 如果文件不存在,将随提供的内容创建. 如果它存在,且导出名称尚未定义,则验证符将被附加到文件中.
[大 这个编码器尊重
validators目录已配置于adonisrc.ts默认为app/validators。 。 。 .
const transformer = new CodeTransformer(appRoot)
try {
await transformer.addValidator({
validatorFileName: 'user.ts',
exportName: 'loginValidator',
contents: `export const loginValidator = vine.compile(
vine.object({
email: vine.string().email(),
password: vine.string().minLength(8)
})
)`
})
} catch (error) {
console.error('Unable to add validator')
console.error(error)
}
产出(应用/验证/用户)
export const loginValidator = vine.compile(
vine.object({
email: vine.string().email(),
password: vine.string().minLength(8)
})
)
添加范围
创建一个新的速率限制文件,或在已有文件中添加限制文件。 如果文件不存在,将随提供的内容创建. 如果存在且导出名称尚未定义, 限制符将附加在文件中 .
[大 在
start目录已配置于adonisrc.ts默认为start。 。 。 .
const transformer = new CodeTransformer(appRoot)
try {
await transformer.addLimiter({
limiterFileName: 'limiters.ts',
exportName: 'apiThrottle',
contents: `export const apiThrottle = limiter.define('api', () => {
return limiter.allowRequests(10).every('1 minute')
})`
})
} catch (error) {
console.error('Unable to add limiter')
console.error(error)
}
产出(启动/限制器.ts)
export const apiThrottle = limiter.define('api', () => {
return limiter.allowRequests(10).every('1 minute')
})
添加模式混合
将一个或多个混音器应用到模型类中. 这把模型的扩展条款与 compose 帮助和应用指定的混音器.
[大 这个代码mod期望模型文件存在一个默认的导出类,扩展一个基类.
const transformer = new CodeTransformer(appRoot)
try {
await transformer.addModelMixins('user.ts', [
{
name: 'SoftDeletes',
importPath: '@adonisjs/lucid/orm/mixins/soft_deletes',
importType: 'named'
},
{
name: 'Sluggable',
importPath: '#mixins/sluggable',
importType: 'default',
args: ['title', '{ strategy: "dbIncrement" }']
}
])
} catch (error) {
console.error('Unable to add mixins to model')
console.error(error)
}
投入(应用程序/模型/用户.ts)
import { BaseModel } from '@adonisjs/lucid/orm'
export default class User extends BaseModel {
// ...
}
产出(应用程序/模型/用户.ts)
import { BaseModel } from '@adonisjs/lucid/orm'
import { compose } from '@adonisjs/core/helpers'
import { SoftDeletes } from '@adonisjs/lucid/orm/mixins/soft_deletes'
import Sluggable from '#mixins/sluggable'
export default class User extends compose(BaseModel, SoftDeletes(), Sluggable(title, { strategy: "dbIncrement" })) {
// ...
}
添加控制器方法
创建一个新的控制器文件,或者在现有控制器类中添加一个方法. 如果控制器文件不存在,将随类和方法而创建. 如果该方法存在且尚未定义,则该方法会被添加到类中.
[大 这个编码器尊重
controllers目录已配置于adonisrc.ts默认为app/controllers。 。 。 .
const transformer = new CodeTransformer(appRoot)
try {
await transformer.addControllerMethod({
controllerFileName: 'users_controller.ts',
className: 'UsersController',
name: 'destroy',
contents: `async destroy({ params, response }: HttpContext) {
const user = await User.findOrFail(params.id)
await user.delete()
return response.noContent()
}`,
imports: [
{ isType: false, isNamed: true, name: 'HttpContext', path: '@adonisjs/core/http' },
{ isType: false, isNamed: false, name: 'User', path: '#models/user' }
]
})
} catch (error) {
console.error('Unable to add controller method')
console.error(error)
}
输出(应用程序/控制器/用户 控制器.ts)
import type { HttpContext } from '@adonisjs/core/http'
import User from '#models/user'
export default class UsersController {
async destroy({ params, response }: HttpContext) {
const user = await User.findOrFail(params.id)
await user.delete()
return response.noContent()
}
}
RcFile 转换器额外方法
这个 RcFileTransformer 类(通过 updateRcFile callback)现在支持额外的进口和钩子管理方法.
添加Named导入
添加一个命名导入到 adonisrc.ts 文档.
const transformer = new CodeTransformer(appRoot)
try {
await transformer.updateRcFile((rcFile) => {
rcFile.addNamedImport('@adonisjs/core/types', ['Middleware', 'Provider'])
})
} catch (error) {
console.error('Unable to add named import')
console.error(error)
}
产出
import { defineConfig } from '@adonisjs/core/app'
import { Middleware, Provider } from '@adonisjs/core/types'
export default defineConfig({
// ...
})
添加默认导入
添加默认导入到 adonisrc.ts 文档.
const transformer = new CodeTransformer(appRoot)
try {
await transformer.updateRcFile((rcFile) => {
rcFile.addDefaultImport('#config/database', 'databaseConfig')
})
} catch (error) {
console.error('Unable to add default import')
console.error(error)
}
产出
import { defineConfig } from '@adonisjs/core/app'
import databaseConfig from '#config/database'
export default defineConfig({
// ...
})
添加 AssemblerHook 语句
添加装配器钩到 adonisrc.ts 文档。 虎克可以被添加为thunk导入(懒活地加载)或者作为直接导入参考的原始值.
const transformer = new CodeTransformer(appRoot)
try {
await transformer.updateRcFile((rcFile) => {
// Add a thunk-style hook (lazy import)
rcFile.addAssemblerHook('onBuildStarting', './commands/build_hook.js')
// Add a raw hook (direct import reference)
rcFile.addAssemblerHook('onBuildCompleted', 'buildCompletedHook', true)
})
} catch (error) {
console.error('Unable to add assembler hook')
console.error(error)
}
产出
import { defineConfig } from '@adonisjs/core/app'
export default defineConfig({
hooks: {
onBuildStarting: [
() => import('./commands/build_hook.js')
],
onBuildCompleted: [
buildCompletedHook
]
}
})
索引生成器
这个 IndexGenerator 是Assembler中的核心概念,用于监视文件系统并从源目录创建桶文件或类型.
例如,框架的核心使用以下配置来生成控制器,事件,以及收听器的管文件.
import hooks from '@adonisjs/assembler/hooks'
export default hooks.init((type, parent, indexGenerator) => {
indexGenerator.add('controllers', {
source: './app/controllers',
importAlias: '#controllers',
as: 'barrelFile',
exportName: 'controllers',
removeSuffix: 'controllers',
output: './.adonisjs/server/controllers.ts',
})
indexGenerator.add('events', {
source: './app/events',
importAlias: '#events',
as: 'barrelFile',
exportName: 'events',
output: './.adonisjs/server/events.ts',
})
indexGenerator.add('listeners', {
source: './app/listeners',
importAlias: '#listeners',
as: 'barrelFile',
exportName: 'listeners',
removeSuffix: 'listener',
output: './.adonisjs/server/listeners.ts',
})
})
一旦配置在 IndexGenerator,它将扫描所需的目录并生成输出文件。 此外,当一个文件被添加或从源目录中删除时,文件监视器会重新触发索引生成.
Barrel 文件生成
Barrel文件通过导出从源目录中相继收集的懒惰导入实体的集合,提供一个单一的切入点. 这个 IndexGenerator 通过扫描嵌入目录和生成映射文件结构的导入映射,实现此进程的自动化.
举例来说,如下: controllers 目录结构 :
app/controllers/
├── auth/
│ ├── login_controller.ts
│ └── register_controller.ts
├── blog/
│ ├── posts_controller.ts
│ └── post_comments_controller.ts
└── users_controller.ts
在使用控制器配置处理时 IndexGenerator 生成一个桶文件,反映目录作为嵌入对象的层次,使用资本化文件名作为属性密钥.
export const controllers = {
auth: {
Login: () => import('#controllers/auth/login_controller'),
Register: () => import('#controllers/auth/register_controller'),
},
blog: {
Posts: () => import('#controllers/blog/posts_controller'),
PostComments: () => import('#controllers/blog/post_comments_controller'),
},
Users: () => import('#controllers/users_controller'),
}
类型生成
要生成类型文件, 请注册一个自定义的回调, 引用一个实例 VirtualFileSystem ,并通过 buffer 对象.
收藏以密钥值对表示:
- 密钥 ——源目录根的相对路径(没有扩展).
- 数值 - 包含文件对象
importPath, (中文(简体) ),relativePath,以及absolutePath。 。 。 .
import hooks from '@adonisjs/assembler/hooks'
export default hooks.init((type, parent, indexGenerator) => {
indexGenerator.add('inertiaPages', {
source: './inertia/pages',
as: (vfs, buffer) => {
buffer.write(`declare module '@adonisjs/inertia' {`).indent()
buffer.write(`export interface Pages {`).indent()
const files = vfs.asList()
Object.keys(files).forEach((filePath) => {
buffer.write(
`'${filePath}': InferPageProps<typeof import('${file.importPath}').default>`
)
})
buffer.dedent().write('}')
buffer.dedent().write('}')
},
output: './.adonisjs/server/inertia_pages.d.ts',
})
})
捐款
AdonisJS的主要目标之一是建立一个充满活力的用户和贡献者社区,他们相信框架的原则.
我们鼓励你读 捐款指南 在对框架作出贡献之前.
行为守则
为确保AdonisJS社区欢迎所有人,请审查并遵守 行为守则。 。 。 .
许可证
AdonisJS Assembler是开源软件,根据该软件许可 麻省理工学院许可证。 。 。 .