Skip to content

Commit 49cea4d

Browse files
committed
feat: add addPolicy codemod
1 parent 5a3d878 commit 49cea4d

File tree

4 files changed

+164
-1
lines changed

4 files changed

+164
-1
lines changed

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,36 @@ export const plugins: Config['plugins'] = [
390390
]
391391
```
392392

393+
### addPolicy
394+
Register AdonisJS bouncer policy to the list of `policies` object exported from the `app/policies/main.ts` file.
395+
396+
> [!IMPORTANT]
397+
> This codemod expects the `app/policies/main.ts` file to exist and must export a `policies` object from it.
398+
399+
```ts
400+
const transformer = new CodeTransformer(appRoot)
401+
402+
try {
403+
await transformer.addPolicy([
404+
{
405+
name: 'PostPolicy',
406+
path: '#policies/post_policy'
407+
}
408+
])
409+
} catch (error) {
410+
console.error('Unable to register policy')
411+
console.error(error)
412+
}
413+
```
414+
415+
Output
416+
417+
```ts
418+
export const policies = {
419+
UserPolicy: () => import('#policies/post_policy')
420+
}
421+
```
422+
393423
## Contributing
394424
One of the primary goals of AdonisJS is to have a vibrant community of users and contributors who believe in the framework's principles.
395425

src/code_transformer/main.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
} from 'ts-morph'
2222

2323
import { RcFileTransformer } from './rc_file_transformer.js'
24-
import type { MiddlewareNode, EnvValidationNode } from '../types.js'
24+
import type { MiddlewareNode, EnvValidationNode, BouncerPolicyNode } from '../types.js'
2525

2626
/**
2727
* This class is responsible for updating
@@ -139,6 +139,25 @@ export class CodeTransformer {
139139
}
140140
}
141141

142+
/**
143+
* Add a policy to the list of pre-registered policy
144+
*/
145+
#addToPoliciesList(file: SourceFile, policyEntry: BouncerPolicyNode) {
146+
const policiesObject = file
147+
.getVariableDeclarationOrThrow('policies')
148+
.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression)
149+
150+
/**
151+
* Only define policy when one with the existing name does not
152+
* exist.
153+
*/
154+
const existingProperty = policiesObject.getProperty(policyEntry.name)
155+
if (!existingProperty) {
156+
const policy = `${policyEntry.name}: () => import('${policyEntry.path}')`
157+
policiesObject!.insertProperty(0, policy)
158+
}
159+
}
160+
142161
/**
143162
* Write a leading comment
144163
*/
@@ -338,4 +357,26 @@ export class CodeTransformer {
338357
file.formatText(this.#editorSettings)
339358
await file.save()
340359
}
360+
361+
/**
362+
* Adds a policy to the list of `policies` object configured
363+
* inside the `app/policies/main.ts` file.
364+
*/
365+
async addPolicies(policies: BouncerPolicyNode[]) {
366+
/**
367+
* Get the `app/policies/main.ts` source file
368+
*/
369+
const kernelUrl = fileURLToPath(new URL('./app/policies/main.ts', this.#cwd))
370+
const file = this.#project.getSourceFileOrThrow(kernelUrl)
371+
372+
/**
373+
* Process each middleware entry
374+
*/
375+
for (const policy of policies) {
376+
this.#addToPoliciesList(file, policy)
377+
}
378+
379+
file.formatText(this.#editorSettings)
380+
await file.save()
381+
}
341382
}

src/types.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,21 @@ export type MiddlewareNode = {
238238
position?: 'before' | 'after'
239239
}
240240

241+
/**
242+
* Policy node to be added to the list of policies.
243+
*/
244+
export type BouncerPolicyNode = {
245+
/**
246+
* Policy name
247+
*/
248+
name: string
249+
250+
/**
251+
* Policy import path
252+
*/
253+
path: string
254+
}
255+
241256
/**
242257
* Defines the structure of an environment variable validation
243258
* definition

tests/code_transformer.spec.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,3 +702,80 @@ test.group('Code transformer | addJapaPlugin', (group) => {
702702
`)
703703
})
704704
})
705+
706+
test.group('Code transformer | addPolicies', (group) => {
707+
group.each.setup(async ({ context }) => setupFakeAdonisproject(context.fs))
708+
709+
test('add policies to polices/main.ts file', async ({ assert, fs }) => {
710+
await fs.create('app/policies/main.ts', `export const policies = {}`)
711+
712+
const transformer = new CodeTransformer(fs.baseUrl)
713+
714+
await transformer.addPolicies([
715+
{
716+
name: 'PostPolicy',
717+
path: '#policies/post_policy',
718+
},
719+
{
720+
name: 'UserPolicy',
721+
path: '#policies/user_policy',
722+
},
723+
])
724+
725+
const file = await fs.contents('app/policies/main.ts')
726+
assert.snapshot(file).matchInline(`
727+
"export const policies = {
728+
UserPolicy: () => import('#policies/user_policy'),
729+
PostPolicy: () => import('#policies/post_policy')
730+
}
731+
"
732+
`)
733+
})
734+
735+
test('throw error when policies/main.ts file is missing', async ({ fs }) => {
736+
const transformer = new CodeTransformer(fs.baseUrl)
737+
738+
await transformer.addPolicies([
739+
{
740+
name: 'PostPolicy',
741+
path: '#policies/post_policy',
742+
},
743+
{
744+
name: 'UserPolicy',
745+
path: '#policies/user_policy',
746+
},
747+
])
748+
}).throws(/Could not find source file in project at the provided path:/)
749+
750+
test('throw error when policies object is not defined', async ({ fs }) => {
751+
await fs.create('app/policies/main.ts', `export const foo = {}`)
752+
const transformer = new CodeTransformer(fs.baseUrl)
753+
754+
await transformer.addPolicies([
755+
{
756+
name: 'PostPolicy',
757+
path: '#policies/post_policy',
758+
},
759+
{
760+
name: 'UserPolicy',
761+
path: '#policies/user_policy',
762+
},
763+
])
764+
}).throws(`Expected to find variable declaration named 'policies'.`)
765+
766+
test('throw error when policies declaration is not an object', async ({ fs }) => {
767+
await fs.create('app/policies/main.ts', `export const policies = []`)
768+
const transformer = new CodeTransformer(fs.baseUrl)
769+
770+
await transformer.addPolicies([
771+
{
772+
name: 'PostPolicy',
773+
path: '#policies/post_policy',
774+
},
775+
{
776+
name: 'UserPolicy',
777+
path: '#policies/user_policy',
778+
},
779+
])
780+
}).throws(/Expected to find an initializer of kind \'ObjectLiteralExpression\'./)
781+
})

0 commit comments

Comments
 (0)