Skip to content

Commit

Permalink
feat(imports): added import extraction and statements
Browse files Browse the repository at this point in the history
  • Loading branch information
tomgobich committed Sep 17, 2024
1 parent d60fbf4 commit 7179d48
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 3 deletions.
85 changes: 85 additions & 0 deletions src/extractors/import_extractor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import Model from "../model/index.js"
import string from '@adonisjs/core/helpers/string'

class ModelImport {
declare name: string
declare namespace: string

isDefault = false
isType = false

constructor(name: string, namespace: string, isDefault: boolean | null = null, isType: boolean | null = null) {
this.name = name
this.namespace = namespace
this.isDefault = isDefault ?? this.isDefault
this.isType = isType ?? this.isType
}

static getStatements(imports: ModelImport[]) {
const groups = this.#getNamespaceGroups(imports)

return Object.values(groups).map((items) => {
const defaultImport = items.find((item) => item.isDefault)?.name
const namedImports = items
.filter((item) => !item.isDefault)
.map((item) => item.name)
.join(', ')

const names = [defaultImport, namedImports && `{ ${namedImports} }`]
.filter(Boolean)
.join(', ')

return `import${items[0].isType ? ' type' : ''} ${names} from '${items[0].namespace}'`
})
}

static #getNamespaceGroups(imports: ModelImport[]) {
return imports.reduce<Record<string, ModelImport[]>>((groups, imp) => {
const group = groups[imp.namespace] || []

group.push(imp)

groups[imp.namespace] = group

return groups
}, {})
}
}

export default class ModelImportManager {
#imports = new Map<string, ModelImport>()

add(value: ModelImport) {
const name = this.#getName(value)
const existing = this.#imports.get(name)

if (existing && existing.isType && !value.isType) {
existing.isType = false
}

this.#imports.set(name, existing ?? value)
}

#getName(value: ModelImport) {
return `${value.name}@${value.namespace}`
}

extract(model: Model) {
this.add(new ModelImport('BaseModel', '@adonisjs/lucid/orm'))
this.add(new ModelImport('column', '@adonisjs/lucid/orm'))

model.columns.map((column) => {
if (column.type === 'DateTime') {
this.add(new ModelImport('DateTime', 'luxon'))
}
})

model.relationships.map((definition) => {
this.add(new ModelImport(definition.relatedModelName, `./${string.snakeCase(definition.relatedModelName)}.js`, true))
this.add(new ModelImport(definition.type, '@adonisjs/lucid/orm'))
this.add(new ModelImport(string.pascalCase(definition.type), '@adonisjs/lucid/types/relations', false, true))
})

return ModelImport.getStatements([...this.#imports.values()])
}
}
5 changes: 5 additions & 0 deletions src/model/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import { generators } from '@adonisjs/core/app'
import ModelColumn from './column.js'
import { TableSchema } from '../db/schema.js'
import ModelRelationship, { ModelRelationshipDefinition } from './relationship.js'
import ModelImportManager from '../extractors/import_extractor.js'

export default class Model {
declare name: string
declare fileName: string
declare tableName: string
declare columns: ModelColumn[]
declare relationships: ModelRelationshipDefinition[]
declare imports: string[]

isPivotTable: boolean = false

Expand All @@ -26,6 +28,7 @@ export default class Model {
for (let model of models) {
const ships = relationships.get(model.name)
const values = [...(ships?.values() || [])]
const importManager = new ModelImportManager()

model.isPivotTable = values.filter((relation) => relation.isManyToMany)?.length >= 2
model.relationships = values.reduce<ModelRelationshipDefinition[]>(
Expand All @@ -34,6 +37,8 @@ export default class Model {
},
[]
)

model.imports = importManager.extract(model)
}

return models.filter((model) => !model.isPivotTable)
Expand Down
2 changes: 2 additions & 0 deletions src/model/relationship.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type ModelRelationshipInfo = {

export type ModelRelationshipDefinition = {
type: RelationshipTypes
relatedModelName: string
decorator: string
property: string
}
Expand Down Expand Up @@ -88,6 +89,7 @@ export default class ModelRelationship {

return {
type: info.type,
relatedModelName: info.relatedModelName,
decorator: `@${info.type}(() => ${info.relatedModelName})`,
property: `declare ${propertyName}: ${string.pascalCase(info.type)}<typeof ${info.relatedModelName}>`,
}
Expand Down
6 changes: 3 additions & 3 deletions stubs/generate/model.stub
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DateTime } from 'luxon'
import { BaseModel, column } from '@adonisjs/lucid/orm'

{{ #each model.imports as statement }}
{{ statement }}{{{ '\n' }}}
{{ /each }}
export default class {{ model.name }} extends BaseModel {{ '{' }}{{ #each model.columns as column }}
{{ column.getDecorator() }}
declare {{ column.name }}: {{ column.type }}
Expand Down

0 comments on commit 7179d48

Please sign in to comment.