Skip to content

Commit ffad783

Browse files
JeanMechealxhub
authored andcommitted
refactor(core): add dedicated deprecated signatures for providedIn: any / NgModule.
Those were deprecated by #47616 back in v15. fixes #65923 (cherry picked from commit 8199945)
1 parent 4bfee61 commit ffad783

File tree

8 files changed

+158
-13
lines changed

8 files changed

+158
-13
lines changed

adev/shared-docs/pipeline/api-gen/rendering/entities/categorization.mts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,9 @@ function getTag<T extends HasJsDocTags | FunctionEntry>(entry: T, tag: string, e
174174
export function getTagSinceVersion<T extends HasJsDocTags>(
175175
entry: T,
176176
tagName: string,
177+
every = false,
177178
): {version: string | undefined} | undefined {
178-
const tag = getTag(entry, tagName);
179+
const tag = getTag(entry, tagName, every);
179180
if (!tag) {
180181
return undefined;
181182
}

adev/shared-docs/pipeline/api-gen/rendering/test/transforms/jsdoc-transforms.spec.mts

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
import {initHighlighter} from '../../../../shared/shiki.mjs';
1010
import {setHighlighterInstance} from '../../shiki/shiki.mjs';
1111
import {setCurrentSymbol, setSymbols} from '../../symbol-context.mjs';
12-
import {addHtmlAdditionalLinks, addHtmlDescription} from '../../transforms/jsdoc-transforms.mjs';
12+
import {
13+
addHtmlAdditionalLinks,
14+
addHtmlDescription,
15+
setEntryFlags,
16+
} from '../../transforms/jsdoc-transforms.mjs';
1317

1418
// @ts-ignore This compiles fine, but Webstorm doesn't like the ESM import in a CJS context.
1519
describe('jsdoc transforms', () => {
@@ -230,4 +234,62 @@ function setupRouter() {
230234

231235
expect(entry.htmlDescription).toContain('/api/angular/router/Router');
232236
});
237+
238+
it('should only mark as deprecated if all overloads are deprecated', () => {
239+
const entry = setEntryFlags({
240+
name: 'Injectable',
241+
jsdocTags: [
242+
{'name': 'see', 'comment': '[Introduction to Services and DI](guide/di)'},
243+
{
244+
'name': 'see',
245+
'comment': '[Creating and using services](guide/di/creating-and-using-services)',
246+
},
247+
],
248+
signatures: [
249+
{
250+
'parameters': [],
251+
'jsdocTags': [{'name': 'see', 'comment': '[Introduction to Services and DI](guide/di)'}],
252+
},
253+
{
254+
'parameters': [],
255+
'jsdocTags': [
256+
{
257+
'name': 'deprecated',
258+
'comment':
259+
"The `providedIn: NgModule` or `providedIn:'any'` options are deprecated. Please use the other signatures.",
260+
},
261+
],
262+
},
263+
],
264+
} as any);
265+
266+
expect(entry.deprecated).toEqual(undefined);
267+
268+
const deprecatedEntry = setEntryFlags({
269+
name: 'Injectable',
270+
jsdocTags: [
271+
{'name': 'see', 'comment': '[Introduction to Services and DI](guide/di)'},
272+
{
273+
'name': 'see',
274+
'comment': '[Creating and using services](guide/di/creating-and-using-services)',
275+
},
276+
],
277+
signatures: [
278+
{
279+
'parameters': [],
280+
'jsdocTags': [
281+
{'name': 'see', 'comment': '[Introduction to Services and DI](guide/di)'},
282+
{'name': 'deprecated', 'comment': '19.0 something something'},
283+
],
284+
},
285+
{
286+
'parameters': [],
287+
'jsdocTags': [{'name': 'deprecated', 'comment': '19.0 something something'}],
288+
},
289+
],
290+
} as any);
291+
292+
// It's deprecated by
293+
expect(deprecatedEntry.deprecated).toEqual({version: '19.0'});
294+
});
233295
});

adev/shared-docs/pipeline/api-gen/rendering/transforms/jsdoc-transforms.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export function setEntryFlags<T extends HasJsDocTags & HasModuleName>(
119119
const deprecationMessage = getDeprecatedEntry(entry);
120120
return {
121121
...entry,
122-
deprecated: getTagSinceVersion(entry, 'deprecated'),
122+
deprecated: getTagSinceVersion(entry, 'deprecated', true),
123123
deprecationMessage: deprecationMessage
124124
? getHtmlForJsDocText(deprecationMessage)
125125
: deprecationMessage,

goldens/public-api/core/index.api.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -884,9 +884,13 @@ export const Injectable: InjectableDecorator;
884884
// @public
885885
export interface InjectableDecorator {
886886
(): TypeDecorator;
887+
// @deprecated (undocumented)
888+
(options?: {
889+
providedIn: Type<any> | 'any';
890+
} & InjectableProvider): TypeDecorator;
887891
// (undocumented)
888892
(options?: {
889-
providedIn: Type<any> | 'root' | 'platform' | 'any' | null;
893+
providedIn: 'root' | 'platform' | null;
890894
} & InjectableProvider): TypeDecorator;
891895
// (undocumented)
892896
new (): Injectable;
@@ -916,6 +920,11 @@ export interface InjectDecorator {
916920

917921
// @public
918922
export class InjectionToken<T> {
923+
// @deprecated
924+
constructor(_desc: string, options: {
925+
providedIn?: Type<any> | 'any';
926+
factory: () => T;
927+
});
919928
constructor(_desc: string, options?: {
920929
providedIn?: Type<any> | 'root' | 'platform' | 'any' | null;
921930
factory: () => T;

packages/compiler-cli/src/ngtsc/docs/src/decorator_extractor.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -202,13 +202,18 @@ function getDecoratorJsDocNode(
202202
const decoratorInterface = getDecoratorInterface(declaration, typeChecker);
203203

204204
// The public-facing JsDoc for each decorator is on one of its interface's call signatures.
205-
const callSignature = decoratorInterface.members
206-
.filter((node) => {
205+
const callSignaturesWithJsDoc = decoratorInterface.members.filter(
206+
(node): node is ts.CallSignatureDeclaration => {
207207
// The description block lives on one of the call signatures for this interface.
208-
return ts.isCallSignatureDeclaration(node) && extractRawJsDoc(node);
209-
})
210-
.at(-1); // Get the last one, as it is the most complete
211-
208+
return ts.isCallSignatureDeclaration(node) && !!extractRawJsDoc(node);
209+
},
210+
);
211+
212+
// There may be multiple call signatures, so we need to find the one with the
213+
// longest JSDoc block. This is the one that is intended for public consumption.
214+
const callSignature = callSignaturesWithJsDoc.sort(
215+
(a, b) => (extractRawJsDoc(b)?.length ?? 0) - (extractRawJsDoc(a)?.length ?? 0),
216+
)[0];
212217
if (!callSignature || !ts.isCallSignatureDeclaration(callSignature)) {
213218
throw new Error(`No call signature with JsDoc on "${name}Decorator"`);
214219
}

packages/compiler-cli/test/ngtsc/doc_extraction/decorator_doc_extraction_spec.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,5 +177,51 @@ runInEachFileSystem(() => {
177177
expect(param1.type).toBe('string');
178178
expect(param1.description).toBe('');
179179
});
180+
181+
it('should extract the decorator with some deprecated signatures', () => {
182+
env.write(
183+
'index.ts',
184+
`
185+
export interface InjectableDecorator {
186+
/**
187+
* Decorator that marks a class as available to be
188+
* provided and injected as a dependency.
189+
*
190+
* @usageNotes
191+
*
192+
* Some usage....
193+
*/
194+
(): TypeDecorator;
195+
/**
196+
* @deprecated deprecated.
197+
*/
198+
(options?: {providedIn: Type<any> | 'any'} & InjectableProvider): TypeDecorator;
199+
(
200+
options?: {providedIn: Type<any> | 'root' | 'platform' | 'any' | null} & InjectableProvider,
201+
): TypeDecorator;
202+
new (): Injectable;
203+
new (
204+
options?: {providedIn: Type<any> | 'root' | 'platform' | 'any' | null} & InjectableProvider,
205+
): Injectable;
206+
}
207+
208+
export interface Injectable {
209+
providedIn?: Type<any> | 'root' | 'platform' | 'any' | null;
210+
}
211+
212+
function makePropDecorator(): InjectDecorator { return () => {}; }
213+
export const Injectable: InjectableDecorator = makeDecorator('Injectable', undefined, undefined, undefined,(type: Type<any>, meta: Injectable) => ({} as any),
214+
);
215+
`,
216+
);
217+
218+
const docs: DocEntry[] = env.driveDocsExtraction('index.ts');
219+
expect(docs.length).toBe(1);
220+
221+
expect(docs[0].name).toBe('Injectable');
222+
expect(docs[0].description).toContain('Decorator that marks a class');
223+
expect(docs[0].entryType).toBe(EntryType.Decorator);
224+
expect(docs[0].jsdocTags[0]).toBeDefined();
225+
});
180226
});
181227
});

packages/core/src/di/injectable.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,12 @@ export interface InjectableDecorator {
6161
*
6262
*/
6363
(): TypeDecorator;
64-
(
65-
options?: {providedIn: Type<any> | 'root' | 'platform' | 'any' | null} & InjectableProvider,
66-
): TypeDecorator;
64+
65+
/**
66+
* @deprecated The `providedIn: NgModule` or `providedIn:'any'` options are deprecated. Please use the other signatures.
67+
*/
68+
(options?: {providedIn: Type<any> | 'any'} & InjectableProvider): TypeDecorator;
69+
(options?: {providedIn: 'root' | 'platform' | null} & InjectableProvider): TypeDecorator;
6770
new (): Injectable;
6871
new (
6972
options?: {providedIn: Type<any> | 'root' | 'platform' | 'any' | null} & InjectableProvider,

packages/core/src/di/injection_token.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,31 @@ export class InjectionToken<T> {
6868

6969
readonly ɵprov: unknown;
7070

71+
/**
72+
* @deprecated The `providedIn: NgModule` or `providedIn:'any'` options are deprecated. Please use the other signature.
73+
*/
74+
constructor(
75+
_desc: string,
76+
options: {
77+
providedIn?: Type<any> | 'any';
78+
factory: () => T;
79+
},
80+
);
81+
7182
/**
7283
* @param _desc Description for the token,
7384
* used only for debugging purposes,
7485
* it should but does not need to be unique
7586
* @param options Options for the token's usage, as described above
7687
*/
88+
constructor(
89+
_desc: string,
90+
options?: {
91+
providedIn?: Type<any> | 'root' | 'platform' | 'any' | null;
92+
factory: () => T;
93+
},
94+
);
95+
7796
constructor(
7897
protected _desc: string,
7998
options?: {

0 commit comments

Comments
 (0)