Skip to content

Commit

Permalink
feat: add getTsMorphProject method (#4414)
Browse files Browse the repository at this point in the history
* feat: add getTsMorphProject method

* fix: typo

* refactor: add explicit return type

Otherwise we have a nasty ` The inferred type of 'getTsMorphProject' cannot be named without a reference to ....`

* chore: remove log
  • Loading branch information
Julien-R44 committed Feb 24, 2024
1 parent db03e4c commit ee96efe
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 40 deletions.
88 changes: 48 additions & 40 deletions modules/ace/codemods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,10 @@ import type { Application } from '../app.js'
* inside user application.
*/
export class Codemods extends EventEmitter {
/**
* Flag to know if assembler is installed as a
* peer dependency or not.
*/
#isAssemblerInstalled?: boolean

/**
* Reference to lazily imported assembler code transformer
*/
#codeTransformer?: typeof import('@adonisjs/assembler/code_transformer')
#codeTransformer?: CodeTransformer

/**
* Reference to AdonisJS application
Expand Down Expand Up @@ -65,12 +59,19 @@ export class Codemods extends EventEmitter {
}

/**
* Lazily imports assembler
* - Lazily import the code transformer
* - Return a fresh or reused instance of the code transformer
*/
async #importAssembler() {
if (this.#isAssemblerInstalled === undefined) {
this.#codeTransformer = await import('@adonisjs/assembler/code_transformer')
this.#isAssemblerInstalled = !!this.#codeTransformer
async #getCodeTransformer() {
try {
if (!this.#codeTransformer) {
const { CodeTransformer } = await import('@adonisjs/assembler/code_transformer')
this.#codeTransformer = new CodeTransformer(this.#app.appRoot)
}

return this.#codeTransformer
} catch {
return null
}
}

Expand Down Expand Up @@ -113,21 +114,38 @@ export class Codemods extends EventEmitter {
this.#cliLogger.action('update .env file').succeeded()
}

/**
* Returns the TsMorph project instance
* See https://ts-morph.com/
*/
async getTsMorphProject(): Promise<
| InstanceType<typeof import('@adonisjs/assembler/code_transformer').CodeTransformer>['project']
| undefined
> {
const transformer = await this.#getCodeTransformer()
if (!transformer) {
this.#cliLogger.warning(
'Cannot create CodeTransformer. Install "@adonisjs/assembler" to modify source files'
)
return
}

return transformer.project
}

/**
* Define validations for the environment variables
*/
async defineEnvValidations(validations: EnvValidationNode) {
await this.#importAssembler()
if (!this.#codeTransformer) {
const transformer = await this.#getCodeTransformer()
if (!transformer) {
this.#cliLogger.warning(
'Cannot update "start/env.ts" file. Install "@adonisjs/assembler" to modify source files'
)
return
}

const transformer = new this.#codeTransformer.CodeTransformer(this.#app.appRoot)
const action = this.#cliLogger.action('update start/env.ts file')

try {
await transformer.defineEnvValidations(validations)
action.succeeded()
Expand All @@ -141,17 +159,15 @@ export class Codemods extends EventEmitter {
* Define validations for the environment variables
*/
async registerMiddleware(stack: 'server' | 'router' | 'named', middleware: MiddlewareNode[]) {
await this.#importAssembler()
if (!this.#codeTransformer) {
const transformer = await this.#getCodeTransformer()
if (!transformer) {
this.#cliLogger.warning(
'Cannot update "start/kernel.ts" file. Install "@adonisjs/assembler" to modify source files'
)
return
}

const transformer = new this.#codeTransformer.CodeTransformer(this.#app.appRoot)
const action = this.#cliLogger.action('update start/kernel.ts file')

try {
await transformer.addMiddlewareToStack(stack, middleware)
action.succeeded()
Expand All @@ -167,17 +183,15 @@ export class Codemods extends EventEmitter {
* file.
*/
async registerPolicies(policies: BouncerPolicyNode[]) {
await this.#importAssembler()
if (!this.#codeTransformer) {
const transformer = await this.#getCodeTransformer()
if (!transformer) {
this.#cliLogger.warning(
'Cannot update "app/policies/main.ts" file. Install "@adonisjs/assembler" to modify source files'
)
return
}

const transformer = new this.#codeTransformer.CodeTransformer(this.#app.appRoot)
const action = this.#cliLogger.action('update app/policies/main.ts file')

try {
await transformer.addPolicies(policies)
action.succeeded()
Expand All @@ -191,15 +205,14 @@ export class Codemods extends EventEmitter {
* Update RCFile
*/
async updateRcFile(...params: Parameters<CodeTransformer['updateRcFile']>) {
await this.#importAssembler()
if (!this.#codeTransformer) {
const transformer = await this.#getCodeTransformer()
if (!transformer) {
this.#cliLogger.warning(
'Cannot update "adonisrc.ts" file. Install "@adonisjs/assembler" to modify source files'
)
return
}

const transformer = new this.#codeTransformer.CodeTransformer(this.#app.appRoot)
const action = this.#cliLogger.action('update adonisrc.ts file')
try {
await transformer.updateRcFile(...params)
Expand All @@ -214,15 +227,14 @@ export class Codemods extends EventEmitter {
* Register a new Vite plugin in the `vite.config.ts` file
*/
async registerVitePlugin(...params: Parameters<CodeTransformer['addVitePlugin']>) {
await this.#importAssembler()
if (!this.#codeTransformer) {
const transformer = await this.#getCodeTransformer()
if (!transformer) {
this.#cliLogger.warning(
'Cannot update "vite.config.ts" file. Install "@adonisjs/assembler" to modify source files'
)
return
}

const transformer = new this.#codeTransformer.CodeTransformer(this.#app.appRoot)
const action = this.#cliLogger.action('update vite.config.ts file')
try {
await transformer.addVitePlugin(...params)
Expand All @@ -237,15 +249,14 @@ export class Codemods extends EventEmitter {
* Register a new Japa plugin in the `tests/bootstrap.ts` file
*/
async registerJapaPlugin(...params: Parameters<CodeTransformer['addJapaPlugin']>) {
await this.#importAssembler()
if (!this.#codeTransformer) {
const transformer = await this.#getCodeTransformer()
if (!transformer) {
this.#cliLogger.warning(
'Cannot update "tests/bootstrap.ts" file. Install "@adonisjs/assembler" to modify source files'
)
return
}

const transformer = new this.#codeTransformer.CodeTransformer(this.#app.appRoot)
const action = this.#cliLogger.action('update tests/bootstrap.ts file')
try {
await transformer.addJapaPlugin(...params)
Expand Down Expand Up @@ -286,13 +297,13 @@ export class Codemods extends EventEmitter {
* ```
*/
async installPackages(packages: { name: string; isDevDependency: boolean }[]) {
await this.#importAssembler()
const transformer = await this.#getCodeTransformer()
const appPath = this.#app.makePath()
const colors = this.#cliLogger.getColors()
const devDependencies = packages.filter((pkg) => pkg.isDevDependency).map(({ name }) => name)
const dependencies = packages.filter((pkg) => !pkg.isDevDependency).map(({ name }) => name)

if (!this.#codeTransformer) {
if (!transformer) {
this.#cliLogger.warning(
'Cannot install packages. Install "@adonisjs/assembler" or manually install following packages'
)
Expand All @@ -301,7 +312,6 @@ export class Codemods extends EventEmitter {
return
}

const transformer = new this.#codeTransformer.CodeTransformer(this.#app.appRoot)
const packageManager = await transformer.detectPackageManager(appPath)

const spinner = this.#cliLogger.await(
Expand Down Expand Up @@ -354,10 +364,8 @@ export class Codemods extends EventEmitter {
const dependencies = packages.filter((pkg) => !pkg.isDevDependency).map(({ name }) => name)

let packageManager: string | null = null
if (this.#codeTransformer) {
const transformer = new this.#codeTransformer.CodeTransformer(this.#app.appRoot)
packageManager = await transformer.detectPackageManager(appPath)
}
const transformer = await this.#getCodeTransformer()
if (transformer) packageManager = await transformer.detectPackageManager(appPath)

this.#cliLogger.log('Please install following packages')
this.#cliLogger.log(
Expand Down
29 changes: 29 additions & 0 deletions tests/ace/codemods.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,35 @@ import { test } from '@japa/runner'
import { Codemods } from '../../modules/ace/codemods.js'
import { AceFactory } from '../../factories/core/ace.js'

test.group('Codemods', (group) => {
group.tap((t) => t.timeout(60 * 1000))

test('get ts morph project', async ({ assert, fs }) => {
await fs.createJson('tsconfig.json', {})

const ace = await new AceFactory().make(fs.baseUrl)
await ace.app.init()

const codemods = new Codemods(ace.app, ace.ui.logger)
const project = await codemods.getTsMorphProject()

assert.exists(project)
})

test('reuse the same CodeTransformer instance', async ({ assert, fs }) => {
await fs.createJson('tsconfig.json', {})

const ace = await new AceFactory().make(fs.baseUrl)
await ace.app.init()

const codemods = new Codemods(ace.app, ace.ui.logger)
const project1 = await codemods.getTsMorphProject()
const project2 = await codemods.getTsMorphProject()

assert.deepEqual(project1, project2)
})
})

test.group('Codemods | environment variables', (group) => {
group.tap((t) => t.timeout(60 * 1000))

Expand Down

0 comments on commit ee96efe

Please sign in to comment.