@@ -15,31 +15,27 @@ function insertDebugNameIntoCallExpression(
1515 const signalExpressionIsRequired = isRequiredSignalFunction ( callExpression . expression ) ;
1616 let configPosition = signalExpressionIsRequired ? 0 : 1 ;
1717
18- const nodeArgs = Array . from ( callExpression . arguments ) ;
19-
2018 // 1. If the call expression has no arguments, we pretend that the config object is at position 0.
2119 // We do this so that we can insert a spread element at the start of the args list in a way where
2220 // undefined can be the first argument but still get tree-shaken out in production builds.
2321 // or
24- // 2. Since `linkedSignal` with computation uses a single object for both computation logic
25- // and options (unlike other signal-based primitives), we set the argument position to 0, i.e.
26- // reusing the computation logic object.
22+ // 2. If the signal has an object-only definition (e.g. `linkedSignal` or `resource`), we set
23+ // the argument position to 0, i.e. reusing the existing object.
2724 const signalExpressionHasNoArguments = callExpression . arguments . length === 0 ;
28- const isLinkedSignal = callExpression . expression . getText ( ) === 'linkedSignal' ;
29- const isComputationLinkedSignal =
30- isLinkedSignal && nodeArgs [ 0 ] . kind === ts . SyntaxKind . ObjectLiteralExpression ;
31- if ( signalExpressionHasNoArguments || isComputationLinkedSignal ) {
25+ const signalWithObjectOnlyDefinition = isSignalWithObjectOnlyDefinition ( callExpression ) ;
26+ if ( signalExpressionHasNoArguments || signalWithObjectOnlyDefinition ) {
3227 configPosition = 0 ;
3328 }
3429
30+ const nodeArgs = Array . from ( callExpression . arguments ) ;
3531 let existingArgument = nodeArgs [ configPosition ] ;
3632
3733 if ( existingArgument === undefined ) {
3834 existingArgument = ts . factory . createObjectLiteralExpression ( [ ] ) ;
3935 }
4036
4137 // Do nothing if an identifier is used as the config object
42- // Ex -
38+ // Ex:
4339 // const defaultObject = { equals: () => false };
4440 // signal(123, defaultObject)
4541 if ( ts . isIdentifier ( existingArgument ) ) {
@@ -104,15 +100,18 @@ function insertDebugNameIntoCallExpression(
104100
105101 let transformedSignalArgs : ts . NodeArray < ts . Expression > ;
106102
107- if ( signalExpressionIsRequired || signalExpressionHasNoArguments || isComputationLinkedSignal ) {
103+ if (
104+ signalExpressionIsRequired ||
105+ signalExpressionHasNoArguments ||
106+ signalWithObjectOnlyDefinition
107+ ) {
108108 // 1. If the call expression is a required signal function, there is no args other than the config object.
109109 // So we just use the spread element as the only argument.
110110 // or
111111 // 2. If the call expression has no arguments (ex. input(), model(), etc), we already added the undefined
112112 // identifier in the spread element above. So we use that spread Element as is.
113113 // or
114- // 3. We are transforming a `linkedSignal` with computation (i.e. we have a single object for both
115- // logic and options).
114+ // 3. We are transforming a signal with object-only definition.
116115 transformedSignalArgs = ts . factory . createNodeArray ( [ spreadElementContainingUpdatedOptions ] ) ;
117116 } else {
118117 // 3. Signal expression is not required and has arguments.
@@ -234,6 +233,23 @@ function isPropertyDeclarationCase(
234233 return ts . isIdentifier ( expression ) && isSignalFunction ( expression ) ;
235234}
236235
236+ type PackageName = 'core' | 'common' ;
237+
238+ const signalFunctions : ReadonlyMap < string , PackageName > = new Map ( [
239+ [ 'signal' , 'core' ] ,
240+ [ 'computed' , 'core' ] ,
241+ [ 'linkedSignal' , 'core' ] ,
242+ [ 'input' , 'core' ] ,
243+ [ 'model' , 'core' ] ,
244+ [ 'viewChild' , 'core' ] ,
245+ [ 'viewChildren' , 'core' ] ,
246+ [ 'contentChild' , 'core' ] ,
247+ [ 'contentChildren' , 'core' ] ,
248+ [ 'effect' , 'core' ] ,
249+ [ 'resource' , 'core' ] ,
250+ [ 'httpResource' , 'common' ] ,
251+ ] ) ;
252+
237253/**
238254 *
239255 * Determines if a node is an expression that references an @angular/core imported symbol.
@@ -243,7 +259,7 @@ function isPropertyDeclarationCase(
243259 * const mySignal = signal(123); // expressionIsUsingAngularImportedSymbol === true
244260 * ```
245261 */
246- function expressionIsUsingAngularCoreImportedSymbol (
262+ function expressionIsUsingAngularImportedSymbol (
247263 program : ts . Program ,
248264 expression : ts . Expression ,
249265) : boolean {
@@ -282,25 +298,14 @@ function expressionIsUsingAngularCoreImportedSymbol(
282298 }
283299
284300 const specifier = importDeclaration . moduleSpecifier . text ;
301+ const packageName = signalFunctions . get ( expression . getText ( ) ) ;
285302 return (
286303 specifier !== undefined &&
287- ( specifier === '@angular/core' || specifier . startsWith ( '@angular/core/' ) )
304+ packageName !== undefined &&
305+ ( specifier === `@angular/${ packageName } ` || specifier . startsWith ( `@angular/${ packageName } /` ) )
288306 ) ;
289307}
290308
291- const signalFunctions : ReadonlySet < string > = new Set ( [
292- 'signal' ,
293- 'computed' ,
294- 'linkedSignal' ,
295- 'input' ,
296- 'model' ,
297- 'viewChild' ,
298- 'viewChildren' ,
299- 'contentChild' ,
300- 'contentChildren' ,
301- 'effect' ,
302- ] ) ;
303-
304309function isSignalFunction ( expression : ts . Identifier ) : boolean {
305310 const text = expression . text ;
306311
@@ -331,10 +336,10 @@ function transformVariableDeclaration(
331336
332337 const expression = node . initializer . expression ;
333338 if ( ts . isPropertyAccessExpression ( expression ) ) {
334- if ( ! expressionIsUsingAngularCoreImportedSymbol ( program , expression . expression ) ) {
339+ if ( ! expressionIsUsingAngularImportedSymbol ( program , expression . expression ) ) {
335340 return node ;
336341 }
337- } else if ( ! expressionIsUsingAngularCoreImportedSymbol ( program , expression ) ) {
342+ } else if ( ! expressionIsUsingAngularImportedSymbol ( program , expression ) ) {
338343 return node ;
339344 }
340345
@@ -362,10 +367,10 @@ function transformPropertyAssignment(
362367) : ts . ExpressionStatement {
363368 const expression = node . expression . right . expression ;
364369 if ( ts . isPropertyAccessExpression ( expression ) ) {
365- if ( ! expressionIsUsingAngularCoreImportedSymbol ( program , expression . expression ) ) {
370+ if ( ! expressionIsUsingAngularImportedSymbol ( program , expression . expression ) ) {
366371 return node ;
367372 }
368- } else if ( ! expressionIsUsingAngularCoreImportedSymbol ( program , expression ) ) {
373+ } else if ( ! expressionIsUsingAngularImportedSymbol ( program , expression ) ) {
369374 return node ;
370375 }
371376
@@ -387,10 +392,10 @@ function transformPropertyDeclaration(
387392
388393 const expression = node . initializer . expression ;
389394 if ( ts . isPropertyAccessExpression ( expression ) ) {
390- if ( ! expressionIsUsingAngularCoreImportedSymbol ( program , expression . expression ) ) {
395+ if ( ! expressionIsUsingAngularImportedSymbol ( program , expression . expression ) ) {
391396 return node ;
392397 }
393- } else if ( ! expressionIsUsingAngularCoreImportedSymbol ( program , expression ) ) {
398+ } else if ( ! expressionIsUsingAngularImportedSymbol ( program , expression ) ) {
394399 return node ;
395400 }
396401
@@ -410,6 +415,24 @@ function transformPropertyDeclaration(
410415 }
411416}
412417
418+ /**
419+ * The function determines whether the target signal has an object-only definition, that includes
420+ * both the computation logic and the options (unlike other signal-based primitives), or not.
421+ * Ex: `linkedSignal` with computation, `resource`
422+ */
423+ function isSignalWithObjectOnlyDefinition ( callExpression : ts . CallExpression ) : boolean {
424+ const callExpressionText = callExpression . expression . getText ( ) ;
425+ const nodeArgs = Array . from ( callExpression . arguments ) ;
426+
427+ const isLinkedSignal = callExpressionText === 'linkedSignal' ;
428+ const isComputationLinkedSignal =
429+ isLinkedSignal && nodeArgs [ 0 ] . kind === ts . SyntaxKind . ObjectLiteralExpression ;
430+
431+ const isResource = callExpressionText === 'resource' ;
432+
433+ return isComputationLinkedSignal || isResource ;
434+ }
435+
413436/**
414437 *
415438 * This transformer adds a debugName property to the config object of signal functions like
0 commit comments