@sqldoc/ns-deprecated 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +30 -0
- package/src/__tests__/deprecated.test.ts +139 -0
- package/src/index.ts +94 -0
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "module",
|
|
3
|
+
"name": "@sqldoc/ns-deprecated",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"description": "Deprecation namespace for sqldoc -- marks SQL objects as deprecated with COMMENT ON statements",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./src/index.ts",
|
|
9
|
+
"import": "./src/index.ts",
|
|
10
|
+
"default": "./src/index.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"main": "./src/index.ts",
|
|
14
|
+
"types": "./src/index.ts",
|
|
15
|
+
"files": [
|
|
16
|
+
"src",
|
|
17
|
+
"package.json"
|
|
18
|
+
],
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"@sqldoc/core": "0.0.1"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"typescript": "^5.9.3",
|
|
24
|
+
"vitest": "^4.1.0",
|
|
25
|
+
"@sqldoc/core": "0.0.1"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"test": "vitest run"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { TagContext } from '@sqldoc/core'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import plugin from '../index'
|
|
4
|
+
|
|
5
|
+
function makeCtx(overrides: Partial<TagContext> = {}): TagContext {
|
|
6
|
+
return {
|
|
7
|
+
target: 'table',
|
|
8
|
+
objectName: 'users',
|
|
9
|
+
tag: { name: '$self', args: {} },
|
|
10
|
+
namespaceTags: [],
|
|
11
|
+
siblingTags: [],
|
|
12
|
+
fileTags: [],
|
|
13
|
+
astNode: null,
|
|
14
|
+
fileStatements: [],
|
|
15
|
+
config: {},
|
|
16
|
+
filePath: 'test.sql',
|
|
17
|
+
...overrides,
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('ns-deprecated plugin', () => {
|
|
22
|
+
it('exports apiVersion === 1', () => {
|
|
23
|
+
expect(plugin.apiVersion).toBe(1)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('exports name === "deprecated"', () => {
|
|
27
|
+
expect(plugin.name).toBe('deprecated')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('has $self, replace, and remove tag entries', () => {
|
|
31
|
+
expect(plugin.tags).toHaveProperty('$self')
|
|
32
|
+
expect(plugin.tags).toHaveProperty('replace')
|
|
33
|
+
expect(plugin.tags).toHaveProperty('remove')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
describe('onTag', () => {
|
|
37
|
+
it('@deprecated on a table generates COMMENT ON TABLE', () => {
|
|
38
|
+
const ctx = makeCtx({
|
|
39
|
+
target: 'table',
|
|
40
|
+
objectName: 'users',
|
|
41
|
+
tag: { name: '$self', args: {} },
|
|
42
|
+
})
|
|
43
|
+
const result = plugin.onTag!(ctx) as any
|
|
44
|
+
expect(result.sql).toEqual([{ sql: `COMMENT ON TABLE "users" IS 'DEPRECATED';` }])
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('@deprecated with null tag name generates COMMENT ON TABLE', () => {
|
|
48
|
+
const ctx = makeCtx({
|
|
49
|
+
target: 'table',
|
|
50
|
+
objectName: 'users',
|
|
51
|
+
tag: { name: null, args: {} },
|
|
52
|
+
})
|
|
53
|
+
const result = plugin.onTag!(ctx) as any
|
|
54
|
+
expect(result.sql).toEqual([{ sql: `COMMENT ON TABLE "users" IS 'DEPRECATED';` }])
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('@deprecated on a column generates COMMENT ON COLUMN with qualified name', () => {
|
|
58
|
+
const ctx = makeCtx({
|
|
59
|
+
target: 'column',
|
|
60
|
+
objectName: 'users',
|
|
61
|
+
columnName: 'email',
|
|
62
|
+
tag: { name: '$self', args: {} },
|
|
63
|
+
})
|
|
64
|
+
const result = plugin.onTag!(ctx) as any
|
|
65
|
+
expect(result.sql).toEqual([{ sql: `COMMENT ON COLUMN "users"."email" IS 'DEPRECATED';` }])
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('@deprecated on a view generates COMMENT ON VIEW', () => {
|
|
69
|
+
const ctx = makeCtx({
|
|
70
|
+
target: 'view',
|
|
71
|
+
objectName: 'active_users',
|
|
72
|
+
tag: { name: '$self', args: {} },
|
|
73
|
+
})
|
|
74
|
+
const result = plugin.onTag!(ctx) as any
|
|
75
|
+
expect(result.sql).toEqual([{ sql: `COMMENT ON VIEW "active_users" IS 'DEPRECATED';` }])
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('@deprecated on a function generates COMMENT ON FUNCTION', () => {
|
|
79
|
+
const ctx = makeCtx({
|
|
80
|
+
target: 'function',
|
|
81
|
+
objectName: 'get_user',
|
|
82
|
+
tag: { name: '$self', args: {} },
|
|
83
|
+
})
|
|
84
|
+
const result = plugin.onTag!(ctx) as any
|
|
85
|
+
expect(result.sql).toEqual([{ sql: `COMMENT ON FUNCTION "get_user" IS 'DEPRECATED';` }])
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('@deprecated on a type generates COMMENT ON TYPE', () => {
|
|
89
|
+
const ctx = makeCtx({
|
|
90
|
+
target: 'type',
|
|
91
|
+
objectName: 'status_enum',
|
|
92
|
+
tag: { name: '$self', args: {} },
|
|
93
|
+
})
|
|
94
|
+
const result = plugin.onTag!(ctx) as any
|
|
95
|
+
expect(result.sql).toEqual([{ sql: `COMMENT ON TYPE "status_enum" IS 'DEPRECATED';` }])
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('@deprecated.replace generates COMMENT with replacement suggestion', () => {
|
|
99
|
+
const ctx = makeCtx({
|
|
100
|
+
target: 'table',
|
|
101
|
+
objectName: 'old_users',
|
|
102
|
+
tag: { name: 'replace', args: ['accounts'] },
|
|
103
|
+
})
|
|
104
|
+
const result = plugin.onTag!(ctx) as any
|
|
105
|
+
expect(result.sql).toEqual([{ sql: `COMMENT ON TABLE "old_users" IS 'DEPRECATED: use accounts instead';` }])
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('@deprecated.remove generates COMMENT with removal date', () => {
|
|
109
|
+
const ctx = makeCtx({
|
|
110
|
+
target: 'table',
|
|
111
|
+
objectName: 'legacy_data',
|
|
112
|
+
tag: { name: 'remove', args: ['2025-06-01'] },
|
|
113
|
+
})
|
|
114
|
+
const result = plugin.onTag!(ctx) as any
|
|
115
|
+
expect(result.sql).toEqual([
|
|
116
|
+
{ sql: `COMMENT ON TABLE "legacy_data" IS 'DEPRECATED: scheduled for removal after 2025-06-01';` },
|
|
117
|
+
])
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('@deprecated.replace on a column generates correct qualified COMMENT', () => {
|
|
121
|
+
const ctx = makeCtx({
|
|
122
|
+
target: 'column',
|
|
123
|
+
objectName: 'users',
|
|
124
|
+
columnName: 'name',
|
|
125
|
+
tag: { name: 'replace', args: ['full_name'] },
|
|
126
|
+
})
|
|
127
|
+
const result = plugin.onTag!(ctx) as any
|
|
128
|
+
expect(result.sql).toEqual([{ sql: `COMMENT ON COLUMN "users"."name" IS 'DEPRECATED: use full_name instead';` }])
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
describe('validation', () => {
|
|
133
|
+
it('@deprecated tags have no validate function (metadata only)', () => {
|
|
134
|
+
expect(plugin.tags.$self!.validate).toBeUndefined()
|
|
135
|
+
expect(plugin.tags.replace.validate).toBeUndefined()
|
|
136
|
+
expect(plugin.tags.remove.validate).toBeUndefined()
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
})
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { NamespacePlugin, TagContext, TagOutput } from '@sqldoc/core'
|
|
2
|
+
|
|
3
|
+
function commentOnSql(target: string, qualifiedName: string, message: string): string {
|
|
4
|
+
return `COMMENT ON ${target} ${qualifiedName} IS '${message}';`
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function targetKeyword(target: string): string {
|
|
8
|
+
switch (target) {
|
|
9
|
+
case 'table':
|
|
10
|
+
return 'TABLE'
|
|
11
|
+
case 'column':
|
|
12
|
+
return 'COLUMN'
|
|
13
|
+
case 'view':
|
|
14
|
+
return 'VIEW'
|
|
15
|
+
case 'function':
|
|
16
|
+
return 'FUNCTION'
|
|
17
|
+
case 'type':
|
|
18
|
+
return 'TYPE'
|
|
19
|
+
default:
|
|
20
|
+
return target.toUpperCase()
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function qualifiedName(ctx: TagContext): string {
|
|
25
|
+
if (ctx.target === 'column') {
|
|
26
|
+
return `"${ctx.objectName}"."${ctx.columnName}"`
|
|
27
|
+
}
|
|
28
|
+
return `"${ctx.objectName}"`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const plugin: NamespacePlugin = {
|
|
32
|
+
apiVersion: 1,
|
|
33
|
+
name: 'deprecated',
|
|
34
|
+
tags: {
|
|
35
|
+
$self: {
|
|
36
|
+
description: 'Mark this SQL object as deprecated',
|
|
37
|
+
targets: ['table', 'column', 'view', 'function', 'type'],
|
|
38
|
+
},
|
|
39
|
+
replace: {
|
|
40
|
+
description: 'Mark as deprecated with a replacement suggestion',
|
|
41
|
+
targets: ['table', 'column', 'view', 'function', 'type'],
|
|
42
|
+
args: [{ type: 'string' }],
|
|
43
|
+
},
|
|
44
|
+
remove: {
|
|
45
|
+
description: 'Mark as deprecated with a scheduled removal date',
|
|
46
|
+
targets: ['table', 'column', 'view', 'function', 'type'],
|
|
47
|
+
args: [{ type: 'string' }],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
onTag(ctx: TagContext): TagOutput | undefined {
|
|
52
|
+
const { tag } = ctx
|
|
53
|
+
const keyword = targetKeyword(ctx.target)
|
|
54
|
+
const name = qualifiedName(ctx)
|
|
55
|
+
const docTarget =
|
|
56
|
+
ctx.target === 'column' ? { object: ctx.objectName, column: ctx.columnName } : { object: ctx.objectName }
|
|
57
|
+
|
|
58
|
+
switch (tag.name) {
|
|
59
|
+
case '$self':
|
|
60
|
+
case null: {
|
|
61
|
+
return {
|
|
62
|
+
sql: [{ sql: commentOnSql(keyword, name, 'DEPRECATED') }],
|
|
63
|
+
docs: {
|
|
64
|
+
columns: [{ header: 'Status', ...docTarget, value: 'Deprecated' }],
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
case 'replace': {
|
|
69
|
+
const args = tag.args as unknown[]
|
|
70
|
+
const newName = args[0] as string
|
|
71
|
+
return {
|
|
72
|
+
sql: [{ sql: commentOnSql(keyword, name, `DEPRECATED: use ${newName} instead`) }],
|
|
73
|
+
docs: {
|
|
74
|
+
columns: [{ header: 'Status', ...docTarget, value: `Deprecated → ${newName}` }],
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
case 'remove': {
|
|
79
|
+
const args = tag.args as unknown[]
|
|
80
|
+
const date = args[0] as string
|
|
81
|
+
return {
|
|
82
|
+
sql: [{ sql: commentOnSql(keyword, name, `DEPRECATED: scheduled for removal after ${date}`) }],
|
|
83
|
+
docs: {
|
|
84
|
+
columns: [{ header: 'Status', ...docTarget, value: `Remove after ${date}` }],
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
default:
|
|
89
|
+
return undefined
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default plugin
|