@openzeppelin/upgrades-core 1.36.0 → 1.37.0
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/CHANGELOG.md +5 -0
- package/dist/cli/cli.test.js +99 -1
- package/dist/cli/cli.test.js.map +1 -1
- package/dist/cli/validate/contract-report.d.ts +2 -1
- package/dist/cli/validate/contract-report.d.ts.map +1 -1
- package/dist/cli/validate/contract-report.js +43 -8
- package/dist/cli/validate/contract-report.js.map +1 -1
- package/dist/cli/validate/default-exclude.d.ts +2 -0
- package/dist/cli/validate/default-exclude.d.ts.map +1 -0
- package/dist/cli/validate/default-exclude.js +6 -0
- package/dist/cli/validate/default-exclude.js.map +1 -0
- package/dist/cli/validate/validate-upgrade-safety.d.ts +2 -1
- package/dist/cli/validate/validate-upgrade-safety.d.ts.map +1 -1
- package/dist/cli/validate/validate-upgrade-safety.js +3 -2
- package/dist/cli/validate/validate-upgrade-safety.js.map +1 -1
- package/dist/cli/validate.d.ts +1 -0
- package/dist/cli/validate.d.ts.map +1 -1
- package/dist/cli/validate.js +38 -16
- package/dist/cli/validate.js.map +1 -1
- package/dist/cli/validate.test.js +20 -0
- package/dist/cli/validate.test.js.map +1 -1
- package/dist/storage/extract.js +1 -1
- package/dist/storage/extract.js.map +1 -1
- package/dist/utils/annotations.js +1 -1
- package/dist/utils/annotations.js.map +1 -1
- package/package.json +4 -3
- package/src/cli/cli.test.ts +141 -1
- package/src/cli/cli.test.ts.md +136 -4
- package/src/cli/cli.test.ts.snap +0 -0
- package/src/cli/validate/contract-report.ts +59 -9
- package/src/cli/validate/default-exclude.ts +3 -0
- package/src/cli/validate/validate-upgrade-safety.ts +3 -1
- package/src/cli/validate.test.ts +22 -0
- package/src/cli/validate.ts +43 -16
- package/src/storage/extract.ts +2 -1
- package/src/utils/annotations.ts +1 -1
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
getContractVersion,
|
|
5
5
|
getStorageLayout,
|
|
6
6
|
ValidationOptions,
|
|
7
|
-
withValidationDefaults,
|
|
8
7
|
Version,
|
|
9
8
|
ValidationData,
|
|
10
9
|
ValidateUpgradeSafetyOptions,
|
|
@@ -18,6 +17,9 @@ import { SourceContract } from './validations';
|
|
|
18
17
|
import { LayoutCompatibilityReport } from '../../storage/report';
|
|
19
18
|
import { indent } from '../../utils/indent';
|
|
20
19
|
import { BuildInfoDictionary, SpecifiedContracts } from './validate-upgrade-safety';
|
|
20
|
+
import { minimatch } from 'minimatch';
|
|
21
|
+
import { ValidateCommandError } from './error';
|
|
22
|
+
import { defaultExclude } from './default-exclude';
|
|
21
23
|
|
|
22
24
|
/**
|
|
23
25
|
* Report for an upgradeable contract.
|
|
@@ -29,7 +31,16 @@ export class UpgradeableContractReport implements Report {
|
|
|
29
31
|
readonly reference: string | undefined,
|
|
30
32
|
readonly standaloneReport: UpgradeableContractErrorReport,
|
|
31
33
|
readonly storageLayoutReport: LayoutCompatibilityReport | undefined,
|
|
32
|
-
) {
|
|
34
|
+
) {
|
|
35
|
+
if (reference === contract) {
|
|
36
|
+
throw new ValidateCommandError(
|
|
37
|
+
`The contract ${contract} must not use itself as a reference for storage layout comparisons.`,
|
|
38
|
+
() => `\
|
|
39
|
+
If this is the first version of your contract, do not specify a reference.
|
|
40
|
+
If this is a subsequent version, keep the previous version of the contract in another file and specify that as the reference, or specify a reference from another build info directory containing the previous version. If you do not have the previous version available, you can skip the storage layout check using the \`unsafeSkipStorageCheck\` option, which is a dangerous option meant to be used as a last resort.`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
33
44
|
|
|
34
45
|
get ok(): boolean {
|
|
35
46
|
return this.standaloneReport.ok && (this.storageLayoutReport === undefined || this.storageLayoutReport.ok);
|
|
@@ -66,12 +77,14 @@ export class UpgradeableContractReport implements Report {
|
|
|
66
77
|
* @param buildInfoDictionary Dictionary of build info directories and the source contracts they contain.
|
|
67
78
|
* @param opts The validation options.
|
|
68
79
|
* @param specifiedContracts If provided, only the specified contract (upgrading from its reference contract) will be reported.
|
|
80
|
+
* @param exclude Exclude validations for contracts in source file paths that match any of the given glob patterns.
|
|
69
81
|
* @returns The upgradeable contract reports.
|
|
70
82
|
*/
|
|
71
83
|
export function getContractReports(
|
|
72
84
|
buildInfoDictionary: BuildInfoDictionary,
|
|
73
85
|
opts: Required<ValidateUpgradeSafetyOptions>,
|
|
74
86
|
specifiedContracts?: SpecifiedContracts,
|
|
87
|
+
exclude?: string[],
|
|
75
88
|
) {
|
|
76
89
|
const upgradeableContractReports: UpgradeableContractReport[] = [];
|
|
77
90
|
|
|
@@ -91,20 +104,43 @@ export function getContractReports(
|
|
|
91
104
|
} else if (specifiedContracts !== undefined || upgradeabilityAssessment.upgradeable) {
|
|
92
105
|
const reference = upgradeabilityAssessment.referenceContract;
|
|
93
106
|
const kind = upgradeabilityAssessment.uups ? 'uups' : 'transparent';
|
|
94
|
-
const report = getUpgradeableContractReport(sourceContract, reference, { ...opts, kind: kind });
|
|
107
|
+
const report = getUpgradeableContractReport(sourceContract, reference, { ...opts, kind: kind }, exclude);
|
|
95
108
|
if (report !== undefined) {
|
|
96
109
|
upgradeableContractReports.push(report);
|
|
110
|
+
} else if (specifiedContracts !== undefined) {
|
|
111
|
+
// If there was no report for the specified contract, it was excluded or is abstract.
|
|
112
|
+
const userAction =
|
|
113
|
+
exclude !== undefined
|
|
114
|
+
? `Ensure the contract is not abstract and is not excluded by the exclude option.`
|
|
115
|
+
: `Ensure the contract is not abstract.`;
|
|
116
|
+
throw new ValidateCommandError(
|
|
117
|
+
`No validation report found for contract ${specifiedContracts.contract.fullyQualifiedName}`,
|
|
118
|
+
() => userAction,
|
|
119
|
+
);
|
|
97
120
|
}
|
|
98
121
|
}
|
|
99
122
|
}
|
|
123
|
+
|
|
100
124
|
return upgradeableContractReports;
|
|
101
125
|
}
|
|
102
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Gets a report for an upgradeable contract.
|
|
129
|
+
* Returns undefined if the contract is excluded or is abstract.
|
|
130
|
+
*/
|
|
103
131
|
function getUpgradeableContractReport(
|
|
104
132
|
contract: SourceContract,
|
|
105
133
|
referenceContract: SourceContract | undefined,
|
|
106
|
-
opts: ValidationOptions
|
|
134
|
+
opts: Required<ValidationOptions>,
|
|
135
|
+
exclude?: string[],
|
|
107
136
|
): UpgradeableContractReport | undefined {
|
|
137
|
+
const excludeWithDefaults = defaultExclude.concat(exclude ?? []);
|
|
138
|
+
|
|
139
|
+
if (excludeWithDefaults.some(glob => minimatch(getPath(contract.fullyQualifiedName), glob))) {
|
|
140
|
+
debug('Excluding contract: ' + contract.fullyQualifiedName);
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
|
|
108
144
|
let version;
|
|
109
145
|
try {
|
|
110
146
|
version = getContractVersion(contract.validationData, contract.fullyQualifiedName);
|
|
@@ -119,7 +155,7 @@ function getUpgradeableContractReport(
|
|
|
119
155
|
}
|
|
120
156
|
|
|
121
157
|
debug('Checking: ' + contract.fullyQualifiedName);
|
|
122
|
-
const standaloneReport = getStandaloneReport(contract.validationData, version, opts);
|
|
158
|
+
const standaloneReport = getStandaloneReport(contract.validationData, version, opts, excludeWithDefaults);
|
|
123
159
|
|
|
124
160
|
let reference: string | undefined;
|
|
125
161
|
let storageLayoutReport: LayoutCompatibilityReport | undefined;
|
|
@@ -135,7 +171,7 @@ function getUpgradeableContractReport(
|
|
|
135
171
|
} else {
|
|
136
172
|
reference = referenceContract.fullyQualifiedName;
|
|
137
173
|
}
|
|
138
|
-
storageLayoutReport = getStorageUpgradeReport(referenceLayout, layout,
|
|
174
|
+
storageLayoutReport = getStorageUpgradeReport(referenceLayout, layout, opts);
|
|
139
175
|
}
|
|
140
176
|
|
|
141
177
|
return new UpgradeableContractReport(contract.fullyQualifiedName, reference, standaloneReport, storageLayoutReport);
|
|
@@ -144,8 +180,22 @@ function getUpgradeableContractReport(
|
|
|
144
180
|
function getStandaloneReport(
|
|
145
181
|
data: ValidationData,
|
|
146
182
|
version: Version,
|
|
147
|
-
opts: ValidationOptions
|
|
183
|
+
opts: Required<ValidationOptions>,
|
|
184
|
+
excludeWithDefaults: string[],
|
|
148
185
|
): UpgradeableContractErrorReport {
|
|
149
|
-
const
|
|
150
|
-
|
|
186
|
+
const allErrors = getErrors(data, version, opts);
|
|
187
|
+
|
|
188
|
+
const includeErrors = allErrors.filter(e => {
|
|
189
|
+
const shouldExclude = excludeWithDefaults.some(glob => minimatch(getPath(e.src), glob));
|
|
190
|
+
if (shouldExclude) {
|
|
191
|
+
debug('Excluding error: ' + e.src);
|
|
192
|
+
}
|
|
193
|
+
return !shouldExclude;
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
return new UpgradeableContractErrorReport(includeErrors);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function getPath(srcOrFullyQualifiedName: string): string {
|
|
200
|
+
return srcOrFullyQualifiedName.split(':')[0];
|
|
151
201
|
}
|
|
@@ -28,6 +28,7 @@ export type SpecifiedContracts = {
|
|
|
28
28
|
* @param reference The name or fully qualified name of the reference contract to use for storage layout comparisons. Can only be used along with `contract`. If not specified, uses the `@custom:oz-upgrades-from` annotation in the contract that is being validated.
|
|
29
29
|
* @param opts Validation options, or undefined to use the default validation options.
|
|
30
30
|
* @param referenceBuildInfoDirs Optional paths of additional build info directories from previous versions of the project to use for storage layout comparisons. When using this option, refer to one of these directories using prefix `<dirName>:` before the contract name or fully qualified name in the `reference` param or `@custom:oz-upgrades-from` annotation, where `<dirName>` is the directory short name. Each directory short name must be unique, including compared to the main build info directory.
|
|
31
|
+
* @param exclude Exclude validations for contracts in source file paths that match any of the given glob patterns.
|
|
31
32
|
* @returns The project report.
|
|
32
33
|
*/
|
|
33
34
|
export async function validateUpgradeSafety(
|
|
@@ -36,6 +37,7 @@ export async function validateUpgradeSafety(
|
|
|
36
37
|
reference?: string,
|
|
37
38
|
opts: ValidateUpgradeSafetyOptions = {},
|
|
38
39
|
referenceBuildInfoDirs?: string[],
|
|
40
|
+
exclude?: string[],
|
|
39
41
|
): Promise<ProjectReport> {
|
|
40
42
|
const allOpts = withCliDefaults(opts);
|
|
41
43
|
|
|
@@ -63,7 +65,7 @@ export async function validateUpgradeSafety(
|
|
|
63
65
|
|
|
64
66
|
const specifiedContracts = findSpecifiedContracts(buildInfoDictionary, allOpts, contract, reference);
|
|
65
67
|
|
|
66
|
-
const contractReports = getContractReports(buildInfoDictionary, allOpts, specifiedContracts);
|
|
68
|
+
const contractReports = getContractReports(buildInfoDictionary, allOpts, specifiedContracts, exclude);
|
|
67
69
|
return getProjectReport(contractReports, specifiedContracts !== undefined);
|
|
68
70
|
}
|
|
69
71
|
|
package/src/cli/validate.test.ts
CHANGED
|
@@ -110,3 +110,25 @@ test('withDefaults - invalid unsafeAllow', t => {
|
|
|
110
110
|
)}`,
|
|
111
111
|
});
|
|
112
112
|
});
|
|
113
|
+
|
|
114
|
+
test('withDefaults - empty unsafeAllow', t => {
|
|
115
|
+
const parsedArgs = minimist(['validate', 'build-info.json', '--unsafeAllow']);
|
|
116
|
+
t.throws(() => withDefaults(parsedArgs), {
|
|
117
|
+
message: `Invalid option: --unsafeAllow cannot be empty`,
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('withDefaults - unsafeAllow multiple times', t => {
|
|
122
|
+
const parsedArgs = minimist([
|
|
123
|
+
'validate',
|
|
124
|
+
'build-info.json',
|
|
125
|
+
'--unsafeAllow',
|
|
126
|
+
'selfdestruct',
|
|
127
|
+
'--unsafeAllow',
|
|
128
|
+
'delegatecall',
|
|
129
|
+
'--unsafeAllow',
|
|
130
|
+
'constructor',
|
|
131
|
+
]);
|
|
132
|
+
const opts = withDefaults(parsedArgs);
|
|
133
|
+
t.deepEqual(opts.unsafeAllow, ['selfdestruct', 'delegatecall', 'constructor']);
|
|
134
|
+
});
|
package/src/cli/validate.ts
CHANGED
|
@@ -16,8 +16,9 @@ Options:
|
|
|
16
16
|
--contract <CONTRACT> The name or fully qualified name of the contract to validate. If not specified, all upgradeable contracts in the build info directory will be validated.
|
|
17
17
|
--reference <REFERENCE_CONTRACT> Can only be used when the --contract option is also provided. The name or fully qualified name of the reference contract to use for storage layout comparisons. If not specified, uses the @custom:oz-upgrades-from annotation if it is defined in the contract that is being validated.
|
|
18
18
|
--requireReference Can only be used when the --contract option is also provided. Not compatible with --unsafeSkipStorageCheck. If specified, requires either the --reference option to be provided or the contract to have a @custom:oz-upgrades-from annotation.
|
|
19
|
-
--referenceBuildInfoDirs Optional paths of additional build info directories from previous versions of the project to use for storage layout comparisons. When using this option, refer to one of these directories using prefix '<dirName>:' before the contract name or fully qualified name in the --reference option or @custom:oz-upgrades-from annotation, where <dirName> is the directory short name. Each directory short name must be unique, including compared to the main build info directory.
|
|
20
|
-
--
|
|
19
|
+
--referenceBuildInfoDirs "<BUILD_INFO_DIR>[,<BUILD_INFO_DIR>...]" Optional paths of additional build info directories from previous versions of the project to use for storage layout comparisons. When using this option, refer to one of these directories using prefix '<dirName>:' before the contract name or fully qualified name in the --reference option or @custom:oz-upgrades-from annotation, where <dirName> is the directory short name. Each directory short name must be unique, including compared to the main build info directory. If passing in multiple directories, separate them with commas or call the option multiple times, once for each directory.
|
|
20
|
+
--exclude "<GLOB_PATTERN>" [--exclude "<GLOB_PATTERN>"...] Exclude validations for contracts in source file paths that match any of the given glob patterns. For example, --exclude "contracts/mocks/**/*.sol". Does not apply to reference contracts. If passing in multiple patterns, call the option multiple times, once for each pattern.
|
|
21
|
+
--unsafeAllow "<VALIDATION_ERROR>[,<VALIDATION_ERROR>...]" Selectively disable one or more validation errors. Comma-separated list with one or more of the following: ${errorKinds.join(
|
|
21
22
|
', ',
|
|
22
23
|
)}
|
|
23
24
|
--unsafeAllowRenames Configure storage layout check to allow variable renaming.
|
|
@@ -34,6 +35,7 @@ export async function main(args: string[]): Promise<void> {
|
|
|
34
35
|
functionArgs.reference,
|
|
35
36
|
functionArgs.opts,
|
|
36
37
|
functionArgs.referenceBuildInfoDirs,
|
|
38
|
+
functionArgs.exclude,
|
|
37
39
|
);
|
|
38
40
|
console.log(result.explain());
|
|
39
41
|
process.exitCode = result.ok ? 0 : 1;
|
|
@@ -50,7 +52,7 @@ function parseArgs(args: string[]) {
|
|
|
50
52
|
'unsafeAllowLinkedLibraries',
|
|
51
53
|
'requireReference',
|
|
52
54
|
],
|
|
53
|
-
string: ['unsafeAllow', 'contract', 'reference', 'referenceBuildInfoDirs'],
|
|
55
|
+
string: ['unsafeAllow', 'contract', 'reference', 'referenceBuildInfoDirs', 'exclude'],
|
|
54
56
|
alias: { h: 'help' },
|
|
55
57
|
});
|
|
56
58
|
const extraArgs = parsedArgs._;
|
|
@@ -74,6 +76,7 @@ interface FunctionArgs {
|
|
|
74
76
|
reference?: string;
|
|
75
77
|
opts: Required<ValidateUpgradeSafetyOptions>;
|
|
76
78
|
referenceBuildInfoDirs?: string[];
|
|
79
|
+
exclude?: string[];
|
|
77
80
|
}
|
|
78
81
|
|
|
79
82
|
/**
|
|
@@ -93,7 +96,8 @@ export function getFunctionArgs(parsedArgs: minimist.ParsedArgs, extraArgs: stri
|
|
|
93
96
|
const contract = getAndValidateString(parsedArgs, 'contract');
|
|
94
97
|
const reference = getAndValidateString(parsedArgs, 'reference');
|
|
95
98
|
const opts = withDefaults(parsedArgs);
|
|
96
|
-
const referenceBuildInfoDirs =
|
|
99
|
+
const referenceBuildInfoDirs = getAndValidateStringArray(parsedArgs, 'referenceBuildInfoDirs', true);
|
|
100
|
+
const exclude = getAndValidateStringArray(parsedArgs, 'exclude', false);
|
|
97
101
|
|
|
98
102
|
if (contract === undefined) {
|
|
99
103
|
if (reference !== undefined) {
|
|
@@ -102,18 +106,45 @@ export function getFunctionArgs(parsedArgs: minimist.ParsedArgs, extraArgs: stri
|
|
|
102
106
|
throw new Error('The --requireReference option can only be used along with the --contract option.');
|
|
103
107
|
}
|
|
104
108
|
}
|
|
105
|
-
return { buildInfoDir, contract, reference, opts, referenceBuildInfoDirs };
|
|
109
|
+
return { buildInfoDir, contract, reference, opts, referenceBuildInfoDirs, exclude };
|
|
106
110
|
}
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
function getAndValidateString(parsedArgs: minimist.ParsedArgs, option: string): string | undefined {
|
|
110
114
|
const value = parsedArgs[option];
|
|
111
|
-
if (value !== undefined
|
|
112
|
-
|
|
115
|
+
if (value !== undefined) {
|
|
116
|
+
assertNonEmptyString(value, option);
|
|
117
|
+
}
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function getAndValidateStringArray(
|
|
122
|
+
parsedArgs: minimist.ParsedArgs,
|
|
123
|
+
option: string,
|
|
124
|
+
useCommaDelimiter: boolean,
|
|
125
|
+
): string[] | undefined {
|
|
126
|
+
const value = parsedArgs[option];
|
|
127
|
+
if (value !== undefined) {
|
|
128
|
+
if (Array.isArray(value)) {
|
|
129
|
+
return value;
|
|
130
|
+
} else {
|
|
131
|
+
assertNonEmptyString(value, option);
|
|
132
|
+
if (useCommaDelimiter) {
|
|
133
|
+
return value.split(/[\s,]+/);
|
|
134
|
+
} else {
|
|
135
|
+
return [value];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
113
138
|
}
|
|
114
139
|
return value;
|
|
115
140
|
}
|
|
116
141
|
|
|
142
|
+
function assertNonEmptyString(value: unknown, option: string) {
|
|
143
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
144
|
+
throw new Error(`Invalid option: --${option} cannot be empty`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
117
148
|
function validateOptions(parsedArgs: minimist.ParsedArgs) {
|
|
118
149
|
const invalidArgs = Object.keys(parsedArgs).filter(
|
|
119
150
|
key =>
|
|
@@ -130,6 +161,7 @@ function validateOptions(parsedArgs: minimist.ParsedArgs) {
|
|
|
130
161
|
'reference',
|
|
131
162
|
'requireReference',
|
|
132
163
|
'referenceBuildInfoDirs',
|
|
164
|
+
'exclude',
|
|
133
165
|
].includes(key),
|
|
134
166
|
);
|
|
135
167
|
if (invalidArgs.length > 0) {
|
|
@@ -137,18 +169,13 @@ function validateOptions(parsedArgs: minimist.ParsedArgs) {
|
|
|
137
169
|
}
|
|
138
170
|
}
|
|
139
171
|
|
|
140
|
-
function getUnsafeAllowKinds(
|
|
172
|
+
function getUnsafeAllowKinds(parseArgs: minimist.ParsedArgs): ValidationError['kind'][] {
|
|
141
173
|
type errorKindsType = (typeof errorKinds)[number];
|
|
142
174
|
|
|
143
|
-
|
|
144
|
-
return [];
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const unsafeAllowTokens: string[] = unsafeAllow.split(/[\s,]+/);
|
|
175
|
+
const unsafeAllowTokens: string[] = getAndValidateStringArray(parseArgs, 'unsafeAllow', true) ?? [];
|
|
148
176
|
if (unsafeAllowTokens.some(token => !errorKinds.includes(token as errorKindsType))) {
|
|
149
|
-
// This includes empty strings
|
|
150
177
|
throw new Error(
|
|
151
|
-
`Invalid option: --unsafeAllow "${unsafeAllow}". Supported values for the --unsafeAllow option are: ${errorKinds.join(
|
|
178
|
+
`Invalid option: --unsafeAllow "${parseArgs['unsafeAllow']}". Supported values for the --unsafeAllow option are: ${errorKinds.join(
|
|
152
179
|
', ',
|
|
153
180
|
)}`,
|
|
154
181
|
);
|
|
@@ -164,7 +191,7 @@ export function withDefaults(parsedArgs: minimist.ParsedArgs): Required<Validate
|
|
|
164
191
|
unsafeSkipStorageCheck: parsedArgs['unsafeSkipStorageCheck'],
|
|
165
192
|
unsafeAllowCustomTypes: parsedArgs['unsafeAllowCustomTypes'],
|
|
166
193
|
unsafeAllowLinkedLibraries: parsedArgs['unsafeAllowLinkedLibraries'],
|
|
167
|
-
unsafeAllow: getUnsafeAllowKinds(parsedArgs
|
|
194
|
+
unsafeAllow: getUnsafeAllowKinds(parsedArgs),
|
|
168
195
|
requireReference: parsedArgs['requireReference'],
|
|
169
196
|
};
|
|
170
197
|
|
package/src/storage/extract.ts
CHANGED
|
@@ -203,7 +203,8 @@ export function loadLayoutType(typeName: TypeName | null | undefined, layout: St
|
|
|
203
203
|
function getRetypedRenamed(varDecl: VariableDeclaration) {
|
|
204
204
|
let retypedFrom, renamedFrom;
|
|
205
205
|
if ('documentation' in varDecl) {
|
|
206
|
-
const docs =
|
|
206
|
+
const docs =
|
|
207
|
+
typeof varDecl.documentation === 'string' ? varDecl.documentation : (varDecl.documentation?.text ?? '');
|
|
207
208
|
for (const { groups } of execall(
|
|
208
209
|
/^\s*(?:@(?<title>\w+)(?::(?<tag>[a-z][a-z-]*))? )?(?<args>(?:(?!^\s@\w+)[^])*)/m,
|
|
209
210
|
docs,
|
package/src/utils/annotations.ts
CHANGED
|
@@ -48,7 +48,7 @@ export function getAnnotationArgs(doc: string, tag: string, supportedArgs?: read
|
|
|
48
48
|
*/
|
|
49
49
|
export function getDocumentation(node: Node) {
|
|
50
50
|
if ('documentation' in node) {
|
|
51
|
-
return typeof node.documentation === 'string' ? node.documentation : node.documentation?.text ?? '';
|
|
51
|
+
return typeof node.documentation === 'string' ? node.documentation : (node.documentation?.text ?? '');
|
|
52
52
|
} else {
|
|
53
53
|
return '';
|
|
54
54
|
}
|