From 2a66a50073b8636d834d28d6e0326f86bb5511f6 Mon Sep 17 00:00:00 2001 From: Guo Yunhe Date: Tue, 22 Sep 2026 12:55:25 +0800 Subject: [PATCH] feat(package.json): add #jobs/* imports --- configure.ts | 36 +++++++++++++++++++++ tests/configure.spec.ts | 71 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/configure.ts b/configure.ts index 567db05..e0f1242 100644 --- a/configure.ts +++ b/configure.ts @@ -7,6 +7,9 @@ * file that was distributed with this source code. */ +import { existsSync } from 'node:fs' +import { readFile, writeFile } from 'node:fs/promises' + import { stubsRoot } from './stubs/main.js' import type Configure from '@adonisjs/core/commands/configure' @@ -22,6 +25,34 @@ const DRIVERS_INFO: Record< database: {}, } +type PackageJson = Record & { + imports?: Record +} + +/** + * Defines the "#jobs/*" subpath import inside the project package.json + * file. Job classes are referenced using this alias (for example inside + * the scheduler preload file), so we make sure it is always defined. + */ +async function defineJobsImport(packageJsonPath: string) { + /** + * Do not create a package.json from scratch. Only update the + * one that already exists. + */ + if (!existsSync(packageJsonPath)) { + return + } + + const packageJson: PackageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8')) + + packageJson.imports = { + ...packageJson.imports, + '#jobs/*': './app/jobs/*.js', + } + + await writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`) +} + /** * Configures the package */ @@ -44,6 +75,11 @@ export async function configure(command: Configure) { .addPreloadFile('#start/scheduler', ['web']) }) + /** + * Define the "#jobs/*" import alias used to reference job classes + */ + await defineJobsImport(command.app.makePath('package.json')) + /** * Define environment variables */ diff --git a/tests/configure.spec.ts b/tests/configure.spec.ts index d9b252e..4de2ab9 100644 --- a/tests/configure.spec.ts +++ b/tests/configure.spec.ts @@ -9,7 +9,7 @@ import { test } from '@japa/runner' import { fileURLToPath } from 'node:url' -import { readdir } from 'node:fs/promises' +import { readdir, readFile } from 'node:fs/promises' import { join } from 'node:path' import { IgnitorFactory } from '@adonisjs/core/factories' import Configure from '@adonisjs/core/commands/configure' @@ -25,6 +25,7 @@ test.group('Configure', (group) => { context.fs.basePath = fileURLToPath(BASE_URL) await context.fs.create('.env', '') + await context.fs.createJson('package.json', { name: 'tmp-app', type: 'module' }) await context.fs.createJson('tsconfig.json', {}) await context.fs.create('start/env.ts', `export default Env.create(new URL('./'), {})`) await context.fs.create('adonisrc.ts', `export default defineConfig({})`) @@ -116,6 +117,74 @@ test.group('Configure', (group) => { await assert.fileContains('start/env.ts', 'QUEUE_DRIVER') }) + test('should define the "#jobs/*" import alias', async ({ assert }) => { + const ignitor = new IgnitorFactory() + .withCoreProviders() + .withCoreConfig() + .create(BASE_URL, { + importer: (filePath) => { + if (filePath.startsWith('./') || filePath.startsWith('../')) { + return import(new URL(filePath, BASE_URL).href) + } + + return import(filePath) + }, + }) + + const app = ignitor.createApp('web') + await app.init().then(() => app.boot()) + + const ace = await app.container.make('ace') + ace.prompt.trap('Select the queue driver you plan to use').chooseOption(0) + ace.ui.switchMode('raw') + + const command = await ace.create(Configure, ['../../index.js']) + await command.exec() + + const packageJson = JSON.parse(await readFile(join(BASE_PATH, 'package.json'), 'utf-8')) + assert.deepEqual(packageJson.imports, { '#jobs/*': './app/jobs/*.js' }) + }) + + test('should preserve existing imports when defining the "#jobs/*" alias', async ({ + assert, + fs, + }) => { + await fs.createJson('package.json', { + name: 'tmp-app', + type: 'module', + imports: { '#controllers/*': './app/controllers/*.js' }, + }) + + const ignitor = new IgnitorFactory() + .withCoreProviders() + .withCoreConfig() + .create(BASE_URL, { + importer: (filePath) => { + if (filePath.startsWith('./') || filePath.startsWith('../')) { + return import(new URL(filePath, BASE_URL).href) + } + + return import(filePath) + }, + }) + + const app = ignitor.createApp('web') + await app.init().then(() => app.boot()) + + const ace = await app.container.make('ace') + ace.prompt.trap('Select the queue driver you plan to use').chooseOption(0) + ace.ui.switchMode('raw') + + const command = await ace.create(Configure, ['../../index.js']) + await command.exec() + + const packageJson = JSON.parse(await readFile(join(BASE_PATH, 'package.json'), 'utf-8')) + assert.deepEqual(packageJson.imports, { + '#controllers/*': './app/controllers/*.js', + '#jobs/*': './app/jobs/*.js', + }) + }) + test('should create redis config when redis driver selected', async ({ assert }) => { const ignitor = new IgnitorFactory() .withCoreProviders()