@adonisjs/assembler
Advanced tools
Comparing version 7.1.1 to 7.2.0
@@ -10,2 +10,73 @@ // src/bundler.ts | ||
// src/hooks.ts | ||
import { RuntimeException } from "@poppinss/utils"; | ||
import Hooks from "@poppinss/hooks"; | ||
var AssemblerHooks = class { | ||
#config; | ||
#hooks = new Hooks(); | ||
constructor(config) { | ||
this.#config = config; | ||
} | ||
/** | ||
* Resolve the hook by importing the file and returning the default export | ||
*/ | ||
async #resolveHookNode(node) { | ||
const exports = await node(); | ||
if (!exports.default) { | ||
throw new RuntimeException("Assembler hook must be defined using the default export"); | ||
} | ||
return exports.default; | ||
} | ||
/** | ||
* Resolve hooks needed for dev-time and register them to the Hooks instance | ||
*/ | ||
async registerDevServerHooks() { | ||
await Promise.all([ | ||
this.#config?.onDevServerStarted?.map( | ||
async (node) => this.#hooks.add("onDevServerStarted", await this.#resolveHookNode(node)) | ||
), | ||
this.#config?.onSourceFileChanged?.map( | ||
async (node) => this.#hooks.add("onSourceFileChanged", await this.#resolveHookNode(node)) | ||
) | ||
]); | ||
} | ||
/** | ||
* Resolve hooks needed for build-time and register them to the Hooks instance | ||
*/ | ||
async registerBuildHooks() { | ||
await Promise.all([ | ||
this.#config?.onBuildStarting?.map( | ||
async (node) => this.#hooks.add("onBuildStarting", await this.#resolveHookNode(node)) | ||
), | ||
this.#config?.onBuildCompleted?.map( | ||
async (node) => this.#hooks.add("onBuildCompleted", await this.#resolveHookNode(node)) | ||
) | ||
]); | ||
} | ||
/** | ||
* When the dev server is started | ||
*/ | ||
async onDevServerStarted(...args) { | ||
await this.#hooks.runner("onDevServerStarted").run(...args); | ||
} | ||
/** | ||
* When a source file changes | ||
*/ | ||
async onSourceFileChanged(...args) { | ||
await this.#hooks.runner("onSourceFileChanged").run(...args); | ||
} | ||
/** | ||
* When the build process is starting | ||
*/ | ||
async onBuildStarting(...args) { | ||
await this.#hooks.runner("onBuildStarting").run(...args); | ||
} | ||
/** | ||
* When the build process is completed | ||
*/ | ||
async onBuildCompleted(...args) { | ||
await this.#hooks.runner("onBuildCompleted").run(...args); | ||
} | ||
}; | ||
// src/helpers.ts | ||
@@ -165,2 +236,3 @@ import { isJunk } from "junk"; | ||
#logger = ui.logger; | ||
#hooks; | ||
#options; | ||
@@ -178,2 +250,3 @@ /** | ||
this.#options = options; | ||
this.#hooks = new AssemblerHooks(options.hooks); | ||
} | ||
@@ -286,2 +359,3 @@ /** | ||
async bundle(stopOnError = true, client) { | ||
await this.#hooks.registerBuildHooks(); | ||
const config = parseConfig(this.#cwd, this.#ts); | ||
@@ -297,2 +371,3 @@ if (!config) { | ||
} | ||
await this.#hooks.onBuildStarting({ colors: ui.colors, logger: this.#logger }); | ||
this.#logger.info("compiling typescript source", { suffix: "tsc" }); | ||
@@ -321,2 +396,3 @@ const buildCompleted = await this.#runTsc(outDir); | ||
this.#logger.log(""); | ||
await this.#hooks.onBuildCompleted({ colors: ui.colors, logger: this.#logger }); | ||
ui.instructions().useRenderer(this.#logger.getRenderer()).heading("Run the following commands to start the server in production").add(this.#colors.cyan(`cd ${this.#getRelativeName(outDir)}`)).add( | ||
@@ -499,2 +575,6 @@ this.#colors.cyan( | ||
/** | ||
* Hooks to execute custom actions during the dev server lifecycle | ||
*/ | ||
#hooks; | ||
/** | ||
* Getting reference to colors library from logger | ||
@@ -508,2 +588,3 @@ */ | ||
this.#options = options; | ||
this.#hooks = new AssemblerHooks(options.hooks); | ||
this.#isMetaFileWithReloadsEnabled = picomatch( | ||
@@ -540,3 +621,3 @@ (this.#options.metaFiles || []).filter(({ reloadServer }) => reloadServer === true).map(({ pattern }) => pattern) | ||
}); | ||
this.#httpServer.on("message", (message) => { | ||
this.#httpServer.on("message", async (message) => { | ||
if (this.#isAdonisJSReadyMessage(message)) { | ||
@@ -553,2 +634,3 @@ const host = message.host === "0.0.0.0" ? "127.0.0.1" : message.host; | ||
displayMessage.render(); | ||
await this.#hooks.onDevServerStarted({ colors: ui3.colors, logger: this.#logger }); | ||
} | ||
@@ -618,2 +700,3 @@ }); | ||
#handleSourceFileChange(action, port, relativePath) { | ||
void this.#hooks.onSourceFileChanged({ colors: ui3.colors, logger: this.#logger }, relativePath); | ||
this.#clearScreen(); | ||
@@ -620,0 +703,0 @@ this.#logger.log(`${this.#colors.green(action)} ${relativePath}`); |
/// <reference types="node" resolution-mode="require"/> | ||
import { installPackage, detectPackageManager } from '@antfu/install-pkg'; | ||
import { Project } from 'ts-morph'; | ||
import { RcFileTransformer } from './rc_file_transformer.js'; | ||
@@ -16,2 +17,6 @@ import type { MiddlewareNode, EnvValidationNode, BouncerPolicyNode } from '../types.js'; | ||
detectPackageManager: typeof detectPackageManager; | ||
/** | ||
* The TsMorph project | ||
*/ | ||
project: Project; | ||
constructor(cwd: URL); | ||
@@ -45,2 +50,10 @@ /** | ||
/** | ||
* Add a new Vite plugin | ||
*/ | ||
addVitePlugin(pluginCall: string, importDeclarations: { | ||
isNamed: boolean; | ||
module: string; | ||
identifier: string; | ||
}[]): Promise<void>; | ||
/** | ||
* Adds a policy to the list of `policies` object configured | ||
@@ -47,0 +60,0 @@ * inside the `app/policies/main.ts` file. |
@@ -276,3 +276,3 @@ // src/code_transformer/main.ts | ||
*/ | ||
#project; | ||
project; | ||
/** | ||
@@ -292,3 +292,3 @@ * Settings to use when persisting files | ||
this.#cwd = cwd; | ||
this.#project = new Project2({ | ||
this.project = new Project2({ | ||
tsConfigFilePath: join(fileURLToPath2(this.#cwd), "tsconfig.json"), | ||
@@ -354,2 +354,27 @@ manipulationSettings: { quoteKind: QuoteKind.Single } | ||
/** | ||
* Add the given import declarations to the source file | ||
* and merge named imports with the existing import | ||
*/ | ||
#addImportDeclarations(file, importDeclarations) { | ||
const existingImports = file.getImportDeclarations(); | ||
importDeclarations.forEach((importDeclaration) => { | ||
const existingImport = existingImports.find( | ||
(mod) => mod.getModuleSpecifierValue() === importDeclaration.module | ||
); | ||
if (existingImport && importDeclaration.isNamed) { | ||
if (!existingImport.getNamedImports().find((namedImport) => namedImport.getName() === importDeclaration.identifier)) { | ||
existingImport.addNamedImport(importDeclaration.identifier); | ||
} | ||
return; | ||
} | ||
if (existingImport) { | ||
return; | ||
} | ||
file.addImportDeclaration({ | ||
...importDeclaration.isNamed ? { namedImports: [importDeclaration.identifier] } : { defaultImport: importDeclaration.identifier }, | ||
moduleSpecifier: importDeclaration.module | ||
}); | ||
}); | ||
} | ||
/** | ||
* Write a leading comment | ||
@@ -369,3 +394,3 @@ */ | ||
const kernelUrl = fileURLToPath2(new URL("./start/env.ts", this.#cwd)); | ||
const file = this.#project.getSourceFileOrThrow(kernelUrl); | ||
const file = this.project.getSourceFileOrThrow(kernelUrl); | ||
const callExpressions = file.getDescendantsOfKind(SyntaxKind2.CallExpression).filter((statement) => statement.getExpression().getText() === "Env.create"); | ||
@@ -412,3 +437,3 @@ if (!callExpressions.length) { | ||
const kernelUrl = fileURLToPath2(new URL("./start/kernel.ts", this.#cwd)); | ||
const file = this.#project.getSourceFileOrThrow(kernelUrl); | ||
const file = this.project.getSourceFileOrThrow(kernelUrl); | ||
for (const middlewareEntry of middleware) { | ||
@@ -428,3 +453,3 @@ if (stack === "named") { | ||
async updateRcFile(callback) { | ||
const rcFileTransformer = new RcFileTransformer(this.#cwd, this.#project); | ||
const rcFileTransformer = new RcFileTransformer(this.#cwd, this.project); | ||
callback(rcFileTransformer); | ||
@@ -438,22 +463,4 @@ await rcFileTransformer.save(); | ||
const testBootstrapUrl = fileURLToPath2(new URL("./tests/bootstrap.ts", this.#cwd)); | ||
const file = this.#project.getSourceFileOrThrow(testBootstrapUrl); | ||
const existingImports = file.getImportDeclarations(); | ||
importDeclarations.forEach((importDeclaration) => { | ||
const existingImport = existingImports.find( | ||
(mod) => mod.getModuleSpecifierValue() === importDeclaration.module | ||
); | ||
if (existingImport && importDeclaration.isNamed) { | ||
if (!existingImport.getNamedImports().find((namedImport) => namedImport.getName() === importDeclaration.identifier)) { | ||
existingImport.addNamedImport(importDeclaration.identifier); | ||
} | ||
return; | ||
} | ||
if (existingImport) { | ||
return; | ||
} | ||
file.addImportDeclaration({ | ||
...importDeclaration.isNamed ? { namedImports: [importDeclaration.identifier] } : { defaultImport: importDeclaration.identifier }, | ||
moduleSpecifier: importDeclaration.module | ||
}); | ||
}); | ||
const file = this.project.getSourceFileOrThrow(testBootstrapUrl); | ||
this.#addImportDeclarations(file, importDeclarations); | ||
const pluginsArray = file.getVariableDeclaration("plugins")?.getInitializerIfKind(SyntaxKind2.ArrayLiteralExpression); | ||
@@ -469,2 +476,26 @@ if (pluginsArray) { | ||
/** | ||
* Add a new Vite plugin | ||
*/ | ||
async addVitePlugin(pluginCall, importDeclarations) { | ||
const viteConfigTsUrl = fileURLToPath2(new URL("./vite.config.ts", this.#cwd)); | ||
const file = this.project.getSourceFile(viteConfigTsUrl); | ||
if (!file) { | ||
throw new Error( | ||
"Cannot find vite.config.ts file. Make sure to rename vite.config.js to vite.config.ts" | ||
); | ||
} | ||
this.#addImportDeclarations(file, importDeclarations); | ||
const defaultExport = file.getDefaultExportSymbol(); | ||
if (!defaultExport) { | ||
throw new Error("Cannot find the default export in vite.config.ts"); | ||
} | ||
const options = defaultExport.getDeclarations()[0].getChildrenOfKind(SyntaxKind2.ObjectLiteralExpression)[0]; | ||
const pluginsArray = options.getPropertyOrThrow("plugins").getFirstChildByKindOrThrow(SyntaxKind2.ArrayLiteralExpression); | ||
if (!pluginsArray.getElements().find((element) => element.getText() === pluginCall)) { | ||
pluginsArray.addElement(pluginCall); | ||
} | ||
file.formatText(this.#editorSettings); | ||
await file.save(); | ||
} | ||
/** | ||
* Adds a policy to the list of `policies` object configured | ||
@@ -475,3 +506,3 @@ * inside the `app/policies/main.ts` file. | ||
const kernelUrl = fileURLToPath2(new URL("./app/policies/main.ts", this.#cwd)); | ||
const file = this.#project.getSourceFileOrThrow(kernelUrl); | ||
const file = this.project.getSourceFileOrThrow(kernelUrl); | ||
for (const policy of policies) { | ||
@@ -478,0 +509,0 @@ this.#addToPoliciesList(file, policy); |
/// <reference types="node" resolution-mode="require"/> | ||
import type { RcFile } from '@adonisjs/application/types'; | ||
/** | ||
@@ -93,2 +94,6 @@ * Options needed to run a script file | ||
assets?: AssetsBundlerOptions; | ||
/** | ||
* Hooks to execute at different stages | ||
*/ | ||
hooks?: Pick<NonNullable<RcFile['unstable_assembler']>, 'onDevServerStarted' | 'onSourceFileChanged'>; | ||
}; | ||
@@ -172,2 +177,6 @@ /** | ||
assets?: AssetsBundlerOptions; | ||
/** | ||
* Hooks to execute at different stages | ||
*/ | ||
hooks?: Pick<NonNullable<RcFile['unstable_assembler']>, 'onBuildCompleted' | 'onBuildStarting'>; | ||
}; | ||
@@ -174,0 +183,0 @@ /** |
{ | ||
"name": "@adonisjs/assembler", | ||
"description": "Provides utilities to run AdonisJS development server and build project for production", | ||
"version": "7.1.1", | ||
"version": "7.2.0", | ||
"engines": { | ||
@@ -37,3 +37,3 @@ "node": ">=18.16.0" | ||
"devDependencies": { | ||
"@adonisjs/application": "^8.0.2", | ||
"@adonisjs/application": "8.1.0", | ||
"@adonisjs/eslint-config": "^1.2.1", | ||
@@ -70,2 +70,4 @@ "@adonisjs/prettier-config": "^1.2.1", | ||
"@poppinss/cliui": "^6.3.0", | ||
"@poppinss/hooks": "^7.2.2", | ||
"@poppinss/utils": "^6.7.2", | ||
"cpy": "^11.0.0", | ||
@@ -72,0 +74,0 @@ "dedent": "^1.5.1", |
@@ -393,2 +393,41 @@ # @adonisjs/assembler | ||
### addVitePlugin | ||
Register a Vite plugin to the `vite.config.ts` file. | ||
> [!IMPORTANT] | ||
> This codemod expects the `vite.config.ts` file to exist and must have the `export default defineConfig` function call. | ||
```ts | ||
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) | ||
} | ||
``` | ||
Output | ||
```ts | ||
import { defineConfig } from 'vite' | ||
import vue from '@vitejs/plugin-vue' | ||
export default defineConfig({ | ||
plugins: [ | ||
vue({ jsx: true }) | ||
] | ||
}) | ||
``` | ||
### addPolicies | ||
@@ -395,0 +434,0 @@ Register AdonisJS bouncer policies to the list of `policies` object exported from the `app/policies/main.ts` file. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
185168
20
2171
483
17
+ Added@poppinss/hooks@^7.2.2
+ Added@poppinss/utils@^6.7.2
+ Added@poppinss/hooks@7.2.4(transitive)