@alloy-js/python 0.4.0-dev.2 → 0.4.0-dev.5

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.
Files changed (258) hide show
  1. package/dist/dev/src/builtins/python.js +30 -0
  2. package/dist/dev/src/builtins/python.js.map +1 -0
  3. package/dist/dev/src/components/Atom.js +122 -0
  4. package/dist/dev/src/components/Atom.js.map +1 -0
  5. package/dist/dev/src/components/CallSignature.js +195 -0
  6. package/dist/dev/src/components/CallSignature.js.map +1 -0
  7. package/dist/dev/src/components/ClassDeclaration.js +112 -0
  8. package/dist/dev/src/components/ClassDeclaration.js.map +1 -0
  9. package/dist/dev/src/components/ClassInstantiation.js +40 -0
  10. package/dist/dev/src/components/ClassInstantiation.js.map +1 -0
  11. package/dist/dev/src/components/ClassMethodDeclaration.js +40 -0
  12. package/dist/dev/src/components/ClassMethodDeclaration.js.map +1 -0
  13. package/dist/dev/src/components/ConstructorDeclaration.js +39 -0
  14. package/dist/dev/src/components/ConstructorDeclaration.js.map +1 -0
  15. package/dist/dev/src/components/DataclassDeclaration.js +177 -0
  16. package/dist/dev/src/components/DataclassDeclaration.js.map +1 -0
  17. package/dist/dev/src/components/Declaration.js +31 -0
  18. package/dist/dev/src/components/Declaration.js.map +1 -0
  19. package/dist/dev/src/components/DunderMethodDeclaration.js +33 -0
  20. package/dist/dev/src/components/DunderMethodDeclaration.js.map +1 -0
  21. package/dist/dev/src/components/EnumDeclaration.js +259 -0
  22. package/dist/dev/src/components/EnumDeclaration.js.map +1 -0
  23. package/dist/dev/src/components/EnumMember.js +95 -0
  24. package/dist/dev/src/components/EnumMember.js.map +1 -0
  25. package/dist/dev/src/components/FunctionBase.js +130 -0
  26. package/dist/dev/src/components/FunctionBase.js.map +1 -0
  27. package/dist/dev/src/components/FunctionCallExpression.js +53 -0
  28. package/dist/dev/src/components/FunctionCallExpression.js.map +1 -0
  29. package/dist/dev/src/components/FunctionDeclaration.js +45 -0
  30. package/dist/dev/src/components/FunctionDeclaration.js.map +1 -0
  31. package/dist/dev/src/components/FutureStatement.js +31 -0
  32. package/dist/dev/src/components/FutureStatement.js.map +1 -0
  33. package/dist/dev/src/components/ImportStatement.js +167 -0
  34. package/dist/dev/src/components/ImportStatement.js.map +1 -0
  35. package/dist/dev/src/components/LexicalScope.js +26 -0
  36. package/dist/dev/src/components/LexicalScope.js.map +1 -0
  37. package/dist/dev/src/components/MemberExpression.js +290 -0
  38. package/dist/dev/src/components/MemberExpression.js.map +1 -0
  39. package/dist/dev/src/components/MemberScope.js +23 -0
  40. package/dist/dev/src/components/MemberScope.js.map +1 -0
  41. package/dist/dev/src/components/MethodBase.js +40 -0
  42. package/dist/dev/src/components/MethodBase.js.map +1 -0
  43. package/dist/dev/src/components/MethodDeclaration.js +38 -0
  44. package/dist/dev/src/components/MethodDeclaration.js.map +1 -0
  45. package/dist/dev/src/components/PropertyDeclaration.js +287 -0
  46. package/dist/dev/src/components/PropertyDeclaration.js.map +1 -0
  47. package/dist/dev/src/components/PyDoc.js +1478 -0
  48. package/dist/dev/src/components/PyDoc.js.map +1 -0
  49. package/dist/dev/src/components/PythonBlock.js +35 -0
  50. package/dist/dev/src/components/PythonBlock.js.map +1 -0
  51. package/dist/dev/src/components/Reference.js +23 -0
  52. package/dist/dev/src/components/Reference.js.map +1 -0
  53. package/dist/dev/src/components/SourceFile.js +385 -0
  54. package/dist/dev/src/components/SourceFile.js.map +1 -0
  55. package/dist/dev/src/components/StatementList.js +34 -0
  56. package/dist/dev/src/components/StatementList.js.map +1 -0
  57. package/dist/dev/src/components/StaticMethodDeclaration.js +40 -0
  58. package/dist/dev/src/components/StaticMethodDeclaration.js.map +1 -0
  59. package/dist/dev/src/components/TypeArguments.js +22 -0
  60. package/dist/dev/src/components/TypeArguments.js.map +1 -0
  61. package/dist/dev/src/components/TypeRefContext.js +33 -0
  62. package/dist/dev/src/components/TypeRefContext.js.map +1 -0
  63. package/dist/dev/src/components/TypeReference.js +67 -0
  64. package/dist/dev/src/components/TypeReference.js.map +1 -0
  65. package/dist/dev/src/components/UnionTypeExpression.js +57 -0
  66. package/dist/dev/src/components/UnionTypeExpression.js.map +1 -0
  67. package/dist/dev/src/components/VariableDeclaration.js +150 -0
  68. package/dist/dev/src/components/VariableDeclaration.js.map +1 -0
  69. package/dist/dev/src/components/index.js +32 -0
  70. package/dist/dev/src/components/index.js.map +1 -0
  71. package/dist/dev/src/context/index.js +2 -0
  72. package/dist/dev/src/context/index.js.map +1 -0
  73. package/dist/dev/src/context/type-ref-context.js +17 -0
  74. package/dist/dev/src/context/type-ref-context.js.map +1 -0
  75. package/dist/dev/src/create-module.js +64 -0
  76. package/dist/dev/src/create-module.js.map +1 -0
  77. package/dist/dev/src/index.js +8 -0
  78. package/dist/dev/src/index.js.map +1 -0
  79. package/dist/dev/src/name-conflict-resolver.js +8 -0
  80. package/dist/dev/src/name-conflict-resolver.js.map +1 -0
  81. package/dist/dev/src/name-policy.js +48 -0
  82. package/dist/dev/src/name-policy.js.map +1 -0
  83. package/dist/dev/src/parameter-descriptor.js +8 -0
  84. package/dist/dev/src/parameter-descriptor.js.map +1 -0
  85. package/dist/dev/src/symbol-creation.js +58 -0
  86. package/dist/dev/src/symbol-creation.js.map +1 -0
  87. package/dist/dev/src/symbols/factories.js +28 -0
  88. package/dist/dev/src/symbols/factories.js.map +1 -0
  89. package/dist/dev/src/symbols/index.js +8 -0
  90. package/dist/dev/src/symbols/index.js.map +1 -0
  91. package/dist/dev/src/symbols/python-lexical-scope.js +15 -0
  92. package/dist/dev/src/symbols/python-lexical-scope.js.map +1 -0
  93. package/dist/dev/src/symbols/python-member-scope.js +7 -0
  94. package/dist/dev/src/symbols/python-member-scope.js.map +1 -0
  95. package/dist/dev/src/symbols/python-module-scope.js +86 -0
  96. package/dist/dev/src/symbols/python-module-scope.js.map +1 -0
  97. package/dist/dev/src/symbols/python-output-symbol.js +73 -0
  98. package/dist/dev/src/symbols/python-output-symbol.js.map +1 -0
  99. package/dist/dev/src/symbols/reference.js +87 -0
  100. package/dist/dev/src/symbols/reference.js.map +1 -0
  101. package/dist/dev/src/symbols/scopes.js +13 -0
  102. package/dist/dev/src/symbols/scopes.js.map +1 -0
  103. package/dist/dev/src/utils.js +13 -0
  104. package/dist/dev/src/utils.js.map +1 -0
  105. package/dist/dev/test/callsignatures.test.js +482 -0
  106. package/dist/dev/test/callsignatures.test.js.map +1 -0
  107. package/dist/dev/test/class-method-declaration.test.js +85 -0
  108. package/dist/dev/test/class-method-declaration.test.js.map +1 -0
  109. package/dist/dev/test/classdeclarations.test.js +654 -0
  110. package/dist/dev/test/classdeclarations.test.js.map +1 -0
  111. package/dist/dev/test/classinstantiations.test.js +281 -0
  112. package/dist/dev/test/classinstantiations.test.js.map +1 -0
  113. package/dist/dev/test/constructordeclaration.test.js +86 -0
  114. package/dist/dev/test/constructordeclaration.test.js.map +1 -0
  115. package/dist/dev/test/dataclassdeclarations.test.js +1068 -0
  116. package/dist/dev/test/dataclassdeclarations.test.js.map +1 -0
  117. package/dist/dev/test/dundermethoddeclaration.test.js +93 -0
  118. package/dist/dev/test/dundermethoddeclaration.test.js.map +1 -0
  119. package/dist/dev/test/enums.test.js +263 -0
  120. package/dist/dev/test/enums.test.js.map +1 -0
  121. package/dist/dev/test/externals.test.js +307 -0
  122. package/dist/dev/test/externals.test.js.map +1 -0
  123. package/dist/dev/test/factories.test.js +122 -0
  124. package/dist/dev/test/factories.test.js.map +1 -0
  125. package/dist/dev/test/functioncallexpressions.test.js +257 -0
  126. package/dist/dev/test/functioncallexpressions.test.js.map +1 -0
  127. package/dist/dev/test/functiondeclaration.test.js +817 -0
  128. package/dist/dev/test/functiondeclaration.test.js.map +1 -0
  129. package/dist/dev/test/imports.test.js +372 -0
  130. package/dist/dev/test/imports.test.js.map +1 -0
  131. package/dist/dev/test/memberexpressions.test.js +1668 -0
  132. package/dist/dev/test/memberexpressions.test.js.map +1 -0
  133. package/dist/dev/test/methoddeclaration.test.js +344 -0
  134. package/dist/dev/test/methoddeclaration.test.js.map +1 -0
  135. package/dist/dev/test/namepolicies.test.js +154 -0
  136. package/dist/dev/test/namepolicies.test.js.map +1 -0
  137. package/dist/dev/test/propertydeclaration.test.js +354 -0
  138. package/dist/dev/test/propertydeclaration.test.js.map +1 -0
  139. package/dist/dev/test/pydocs.test.js +1675 -0
  140. package/dist/dev/test/pydocs.test.js.map +1 -0
  141. package/dist/dev/test/references.test.js +66 -0
  142. package/dist/dev/test/references.test.js.map +1 -0
  143. package/dist/dev/test/sourcefiles.test.js +1802 -0
  144. package/dist/dev/test/sourcefiles.test.js.map +1 -0
  145. package/dist/dev/test/staticmethoddeclaration.test.js +85 -0
  146. package/dist/dev/test/staticmethoddeclaration.test.js.map +1 -0
  147. package/dist/dev/test/type-checking-imports.test.js +617 -0
  148. package/dist/dev/test/type-checking-imports.test.js.map +1 -0
  149. package/dist/dev/test/typereference.test.js +79 -0
  150. package/dist/dev/test/typereference.test.js.map +1 -0
  151. package/dist/dev/test/uniontypeexpression.test.js +307 -0
  152. package/dist/dev/test/uniontypeexpression.test.js.map +1 -0
  153. package/dist/dev/test/utils.js +100 -0
  154. package/dist/dev/test/utils.js.map +1 -0
  155. package/dist/dev/test/values.test.js +182 -0
  156. package/dist/dev/test/values.test.js.map +1 -0
  157. package/dist/dev/test/variables.test.js +363 -0
  158. package/dist/dev/test/variables.test.js.map +1 -0
  159. package/dist/src/components/CallSignature.d.ts.map +1 -1
  160. package/dist/src/components/CallSignature.js +12 -3
  161. package/dist/src/components/CallSignature.js.map +1 -1
  162. package/dist/src/components/ImportStatement.d.ts +12 -0
  163. package/dist/src/components/ImportStatement.d.ts.map +1 -1
  164. package/dist/src/components/ImportStatement.js +47 -5
  165. package/dist/src/components/ImportStatement.js.map +1 -1
  166. package/dist/src/components/MemberExpression.d.ts +1 -1
  167. package/dist/src/components/MemberExpression.d.ts.map +1 -1
  168. package/dist/src/components/MemberExpression.js +98 -180
  169. package/dist/src/components/MemberExpression.js.map +1 -1
  170. package/dist/src/components/Reference.d.ts.map +1 -1
  171. package/dist/src/components/Reference.js +5 -1
  172. package/dist/src/components/Reference.js.map +1 -1
  173. package/dist/src/components/SourceFile.d.ts +1 -1
  174. package/dist/src/components/SourceFile.d.ts.map +1 -1
  175. package/dist/src/components/SourceFile.js +46 -7
  176. package/dist/src/components/SourceFile.js.map +1 -1
  177. package/dist/src/components/TypeRefContext.d.ts +26 -0
  178. package/dist/src/components/TypeRefContext.d.ts.map +1 -0
  179. package/dist/src/components/TypeRefContext.js +29 -0
  180. package/dist/src/components/TypeRefContext.js.map +1 -0
  181. package/dist/src/components/TypeReference.d.ts +5 -0
  182. package/dist/src/components/TypeReference.d.ts.map +1 -1
  183. package/dist/src/components/TypeReference.js +19 -9
  184. package/dist/src/components/TypeReference.js.map +1 -1
  185. package/dist/src/components/VariableDeclaration.d.ts.map +1 -1
  186. package/dist/src/components/VariableDeclaration.js +7 -2
  187. package/dist/src/components/VariableDeclaration.js.map +1 -1
  188. package/dist/src/components/index.d.ts +1 -1
  189. package/dist/src/components/index.d.ts.map +1 -1
  190. package/dist/src/components/index.js +1 -1
  191. package/dist/src/components/index.js.map +1 -1
  192. package/dist/src/context/index.d.ts +2 -0
  193. package/dist/src/context/index.d.ts.map +1 -0
  194. package/dist/src/context/index.js +2 -0
  195. package/dist/src/context/index.js.map +1 -0
  196. package/dist/src/context/type-ref-context.d.ts +13 -0
  197. package/dist/src/context/type-ref-context.d.ts.map +1 -0
  198. package/dist/src/context/type-ref-context.js +17 -0
  199. package/dist/src/context/type-ref-context.js.map +1 -0
  200. package/dist/src/symbols/python-module-scope.d.ts +13 -1
  201. package/dist/src/symbols/python-module-scope.d.ts.map +1 -1
  202. package/dist/src/symbols/python-module-scope.js +36 -2
  203. package/dist/src/symbols/python-module-scope.js.map +1 -1
  204. package/dist/src/symbols/python-output-symbol.d.ts +11 -0
  205. package/dist/src/symbols/python-output-symbol.d.ts.map +1 -1
  206. package/dist/src/symbols/python-output-symbol.js +26 -2
  207. package/dist/src/symbols/python-output-symbol.js.map +1 -1
  208. package/dist/src/symbols/reference.d.ts +8 -1
  209. package/dist/src/symbols/reference.d.ts.map +1 -1
  210. package/dist/src/symbols/reference.js +4 -2
  211. package/dist/src/symbols/reference.js.map +1 -1
  212. package/dist/test/dataclassdeclarations.test.js +5 -2
  213. package/dist/test/dataclassdeclarations.test.js.map +1 -1
  214. package/dist/test/externals.test.js +8 -2
  215. package/dist/test/externals.test.js.map +1 -1
  216. package/dist/test/functiondeclaration.test.js +6 -3
  217. package/dist/test/functiondeclaration.test.js.map +1 -1
  218. package/dist/test/imports.test.js +3 -3
  219. package/dist/test/imports.test.js.map +1 -1
  220. package/dist/test/references.test.js +1 -1
  221. package/dist/test/references.test.js.map +1 -1
  222. package/dist/test/sourcefiles.test.js +26 -26
  223. package/dist/test/sourcefiles.test.js.map +1 -1
  224. package/dist/test/type-checking-imports.test.d.ts +2 -0
  225. package/dist/test/type-checking-imports.test.d.ts.map +1 -0
  226. package/dist/test/type-checking-imports.test.js +437 -0
  227. package/dist/test/type-checking-imports.test.js.map +1 -0
  228. package/dist/test/uniontypeexpression.test.js +4 -1
  229. package/dist/test/uniontypeexpression.test.js.map +1 -1
  230. package/dist/test/variables.test.js +4 -1
  231. package/dist/test/variables.test.js.map +1 -1
  232. package/dist/tsconfig.tsbuildinfo +1 -1
  233. package/package.json +6 -4
  234. package/src/components/CallSignature.tsx +6 -2
  235. package/src/components/ImportStatement.tsx +52 -5
  236. package/src/components/MemberExpression.tsx +174 -298
  237. package/src/components/Reference.tsx +3 -1
  238. package/src/components/SourceFile.tsx +44 -8
  239. package/src/components/TypeRefContext.tsx +36 -0
  240. package/src/components/TypeReference.tsx +15 -7
  241. package/src/components/VariableDeclaration.tsx +5 -1
  242. package/src/components/index.ts +1 -1
  243. package/src/context/index.ts +1 -0
  244. package/src/context/type-ref-context.tsx +16 -0
  245. package/src/symbols/python-module-scope.ts +55 -2
  246. package/src/symbols/python-output-symbol.ts +32 -1
  247. package/src/symbols/reference.tsx +10 -0
  248. package/temp/api.json +443 -338
  249. package/test/dataclassdeclarations.test.tsx +8 -2
  250. package/test/externals.test.tsx +8 -2
  251. package/test/functiondeclaration.test.tsx +6 -3
  252. package/test/imports.test.tsx +6 -6
  253. package/test/references.test.tsx +1 -1
  254. package/test/sourcefiles.test.tsx +13 -13
  255. package/test/type-checking-imports.test.tsx +363 -0
  256. package/test/uniontypeexpression.test.tsx +4 -1
  257. package/test/variables.test.tsx +4 -1
  258. package/vitest.config.ts +8 -0
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  childrenArray,
3
3
  ComponentContext,
4
+ computed,
4
5
  SourceFile as CoreSourceFile,
5
6
  createNamedContext,
6
7
  createScope,
@@ -14,8 +15,12 @@ import {
14
15
  } from "@alloy-js/core";
15
16
  import { join } from "pathe";
16
17
  import { PythonModuleScope } from "../symbols/index.js";
17
- import { ImportStatements } from "./ImportStatement.js";
18
+ import {
19
+ categorizeImportRecords,
20
+ ImportStatements,
21
+ } from "./ImportStatement.js";
18
22
  import { SimpleCommentBlock } from "./PyDoc.js";
23
+ import { PythonBlock } from "./PythonBlock.js";
19
24
  import { Reference } from "./Reference.js";
20
25
 
21
26
  // Non top-level definitions
@@ -99,7 +104,7 @@ export interface SourceFileProps {
99
104
  /**
100
105
  * __future__ imports to render after the docstring but before regular imports.
101
106
  */
102
- futureImports?: Children;
107
+ futureImports?: Children[];
103
108
  }
104
109
 
105
110
  /**
@@ -167,6 +172,28 @@ export function SourceFile(props: SourceFileProps) {
167
172
  props.doc !== undefined ||
168
173
  props.futureImports !== undefined;
169
174
 
175
+ const imports = computed(() => {
176
+ // Quick scan for any type-only imports
177
+ const hasTypeImports = [...scope.importedModules.values()].some(
178
+ (props) =>
179
+ props.symbols && [...props.symbols].some((s) => s.local.isTypeOnly),
180
+ );
181
+
182
+ // Add TYPE_CHECKING before categorizing so it's naturally included
183
+ const typeImportSymbol = hasTypeImports ? scope.addTypeImport() : undefined;
184
+
185
+ // Single categorize - TYPE_CHECKING is already in scope.importedModules
186
+ const { valueImports, typeImports } = categorizeImportRecords(
187
+ scope.importedModules,
188
+ );
189
+
190
+ return {
191
+ valueImports,
192
+ typeImports,
193
+ typeImportSymbol,
194
+ };
195
+ });
196
+
170
197
  return (
171
198
  <CoreSourceFile
172
199
  path={props.path}
@@ -216,12 +243,21 @@ export function SourceFile(props: SourceFileProps) {
216
243
  <hbr />
217
244
  </Show>
218
245
  </Show>
219
- <Show when={scope.importedModules.size > 0}>
220
- <ImportStatements records={scope.importedModules} />
221
- <Show when={hasChildren}>
222
- <hbr />
223
- <hbr />
224
- </Show>
246
+ {/* Regular (non-type-only) imports */}
247
+ <Show when={imports.value.valueImports.size > 0}>
248
+ <ImportStatements records={imports.value.valueImports} />
249
+ <hbr />
250
+ </Show>
251
+ {/* TYPE_CHECKING block with type-only imports */}
252
+ <Show when={imports.value.typeImports.size > 0}>
253
+ <hbr />
254
+ <PythonBlock opener={`if ${imports.value.typeImportSymbol!.name}:`}>
255
+ <ImportStatements records={imports.value.typeImports} />
256
+ </PythonBlock>
257
+ </Show>
258
+ {/* Spacing after imports */}
259
+ <Show when={hasChildren && scope.importedModules.size > 0}>
260
+ <hbr />
225
261
  </Show>
226
262
  {/* Extra blank line before top-level definitions */}
227
263
  <Show
@@ -0,0 +1,36 @@
1
+ import type { Children } from "@alloy-js/core";
2
+ import {
3
+ isTypeRefContext,
4
+ TypeRefContextDef,
5
+ } from "../context/type-ref-context.js";
6
+
7
+ // Re-export for external use
8
+ export { isTypeRefContext };
9
+
10
+ export interface TypeRefContextProps {
11
+ /**
12
+ * Children
13
+ */
14
+ children: Children;
15
+ }
16
+
17
+ /**
18
+ * Set the current context of reference to be type reference.
19
+ *
20
+ * @remarks
21
+ * References used inside the children of this component will be treated as
22
+ * type-only references. When a symbol is only referenced in type contexts,
23
+ * it will be imported inside a `if TYPE_CHECKING:` block.
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * <TypeRefContext>
28
+ * {someTypeRefkey}
29
+ * </TypeRefContext>
30
+ * ```
31
+ */
32
+ export const TypeRefContext = ({ children }: TypeRefContextProps) => {
33
+ return (
34
+ <TypeRefContextDef.Provider value>{children}</TypeRefContextDef.Provider>
35
+ );
36
+ };
@@ -1,5 +1,6 @@
1
1
  import { Children, Refkey, Show } from "@alloy-js/core";
2
2
  import { TypeArguments } from "./TypeArguments.js";
3
+ import { TypeRefContext } from "./TypeRefContext.js";
3
4
 
4
5
  export interface TypeReferenceProps {
5
6
  /** A refkey to a declared symbol. */
@@ -12,6 +13,11 @@ export interface TypeReferenceProps {
12
13
 
13
14
  /**
14
15
  * A type reference like Foo[T, P] or int.
16
+ *
17
+ * @remarks
18
+ * This component automatically wraps its content in a type reference context,
19
+ * so any symbols referenced via refkey will be imported as type-only
20
+ * (inside a `if TYPE_CHECKING:` block) unless also used as values elsewhere.
15
21
  */
16
22
  export function TypeReference(props: TypeReferenceProps) {
17
23
  const type = props.refkey ? props.refkey : props.name;
@@ -21,13 +27,15 @@ export function TypeReference(props: TypeReferenceProps) {
21
27
  : undefined;
22
28
 
23
29
  return (
24
- <group>
25
- <indent>
30
+ <TypeRefContext>
31
+ <group>
32
+ <indent>
33
+ <sbr />
34
+ {type}
35
+ <Show when={Boolean(typeArgs)}>{typeArgs}</Show>
36
+ </indent>
26
37
  <sbr />
27
- {type}
28
- <Show when={Boolean(typeArgs)}>{typeArgs}</Show>
29
- </indent>
30
- <sbr />
31
- </group>
38
+ </group>
39
+ </TypeRefContext>
32
40
  );
33
41
  }
@@ -10,6 +10,7 @@ import { createPythonSymbol } from "../symbol-creation.js";
10
10
  import { Atom } from "./Atom.jsx";
11
11
  import { BaseDeclarationProps } from "./Declaration.jsx";
12
12
  import { SimpleCommentBlock } from "./PyDoc.jsx";
13
+ import { TypeRefContext } from "./TypeRefContext.jsx";
13
14
 
14
15
  export interface VariableDeclarationProps extends BaseDeclarationProps {
15
16
  /**
@@ -93,7 +94,10 @@ export function VariableDeclaration(props: VariableDeclarationProps) {
93
94
  if (!props.type || props.callStatementVar) return undefined;
94
95
  return (
95
96
  <>
96
- : <TypeSymbolSlot>{props.type}</TypeSymbolSlot>
97
+ :{" "}
98
+ <TypeRefContext>
99
+ <TypeSymbolSlot>{props.type}</TypeSymbolSlot>
100
+ </TypeRefContext>
97
101
  </>
98
102
  );
99
103
  });
@@ -13,7 +13,6 @@ export type { CommonFunctionProps } from "./FunctionBase.js";
13
13
  export * from "./FunctionCallExpression.js";
14
14
  export * from "./FunctionDeclaration.js";
15
15
  export * from "./FutureStatement.js";
16
- export * from "./ImportStatement.js";
17
16
  export * from "./LexicalScope.js";
18
17
  export * from "./MemberExpression.js";
19
18
  export * from "./MemberScope.jsx";
@@ -27,6 +26,7 @@ export * from "./SourceFile.js";
27
26
  export * from "./StatementList.js";
28
27
  export * from "./StaticMethodDeclaration.js";
29
28
  export * from "./TypeArguments.js";
29
+ export * from "./TypeRefContext.js";
30
30
  export * from "./TypeReference.js";
31
31
  export * from "./UnionTypeExpression.js";
32
32
  export * from "./VariableDeclaration.js";
@@ -0,0 +1 @@
1
+ export * from "./type-ref-context.js";
@@ -0,0 +1,16 @@
1
+ import { ComponentContext, createContext, useContext } from "@alloy-js/core";
2
+
3
+ /**
4
+ * Context for tracking whether we are in a type annotation position.
5
+ * Used to determine if imports should be guarded with TYPE_CHECKING.
6
+ *
7
+ * @internal Use the TypeRefContext component instead.
8
+ */
9
+ export const TypeRefContextDef: ComponentContext<true> = createContext<true>();
10
+
11
+ /**
12
+ * @returns 'true' if in a type context, 'false' if in a value context.
13
+ */
14
+ export function isTypeRefContext(): boolean {
15
+ return useContext(TypeRefContextDef) === true;
16
+ }
@@ -2,6 +2,30 @@ import { createSymbol, reactive, shallowReactive } from "@alloy-js/core";
2
2
  import { PythonLexicalScope } from "./python-lexical-scope.js";
3
3
  import { PythonOutputSymbol } from "./python-output-symbol.js";
4
4
 
5
+ // Internal typing module for TYPE_CHECKING imports
6
+ let _typingModuleScope: PythonModuleScope | undefined;
7
+ let _typeCheckingSymbol: PythonOutputSymbol | undefined;
8
+
9
+ /**
10
+ * Get the internal typing module scope and TYPE_CHECKING symbol.
11
+ * Used by addTypeImport() to add TYPE_CHECKING imports without
12
+ * going through the binder's refkey resolution.
13
+ */
14
+ function getTypingModuleInternal(): {
15
+ scope: PythonModuleScope;
16
+ TYPE_CHECKING: PythonOutputSymbol;
17
+ } {
18
+ if (!_typingModuleScope) {
19
+ _typingModuleScope = new PythonModuleScope("typing", undefined);
20
+ _typeCheckingSymbol = new PythonOutputSymbol(
21
+ "TYPE_CHECKING",
22
+ _typingModuleScope.symbols,
23
+ {},
24
+ );
25
+ }
26
+ return { scope: _typingModuleScope, TYPE_CHECKING: _typeCheckingSymbol! };
27
+ }
28
+
5
29
  export class ImportedSymbol {
6
30
  local: PythonOutputSymbol;
7
31
  target: PythonOutputSymbol;
@@ -22,6 +46,14 @@ export interface ImportRecordProps {
22
46
 
23
47
  export class ImportRecords extends Map<PythonModuleScope, ImportRecordProps> {}
24
48
 
49
+ export interface AddImportOptions {
50
+ /**
51
+ * If true, this import is only used in type annotation contexts.
52
+ * Such imports will be guarded with `if TYPE_CHECKING:`.
53
+ */
54
+ type?: boolean;
55
+ }
56
+
25
57
  export class PythonModuleScope extends PythonLexicalScope {
26
58
  #importedSymbols: Map<PythonOutputSymbol, PythonOutputSymbol> =
27
59
  shallowReactive(new Map());
@@ -34,9 +66,17 @@ export class PythonModuleScope extends PythonLexicalScope {
34
66
  return this.#importedModules;
35
67
  }
36
68
 
37
- addImport(targetSymbol: PythonOutputSymbol, targetModule: PythonModuleScope) {
69
+ addImport(
70
+ targetSymbol: PythonOutputSymbol,
71
+ targetModule: PythonModuleScope,
72
+ options?: AddImportOptions,
73
+ ) {
38
74
  const existing = this.importedSymbols.get(targetSymbol);
39
75
  if (existing) {
76
+ // If existing is type-only but now used as value, upgrade it
77
+ if (!options?.type && existing.isTypeOnly) {
78
+ existing.markAsValue();
79
+ }
40
80
  return existing;
41
81
  }
42
82
 
@@ -50,7 +90,11 @@ export class PythonModuleScope extends PythonLexicalScope {
50
90
  PythonOutputSymbol,
51
91
  targetSymbol.name,
52
92
  this.symbols,
53
- { binder: this.binder, aliasTarget: targetSymbol },
93
+ {
94
+ binder: this.binder,
95
+ aliasTarget: targetSymbol,
96
+ typeOnly: options?.type,
97
+ },
54
98
  );
55
99
 
56
100
  this.importedSymbols.set(targetSymbol, localSymbol);
@@ -62,6 +106,15 @@ export class PythonModuleScope extends PythonLexicalScope {
62
106
  return localSymbol;
63
107
  }
64
108
 
109
+ /**
110
+ * Add TYPE_CHECKING import from the typing module.
111
+ * Returns the local symbol for use in the if block opener.
112
+ */
113
+ addTypeImport(): PythonOutputSymbol {
114
+ const typing = getTypingModuleInternal();
115
+ return this.addImport(typing.TYPE_CHECKING, typing.scope);
116
+ }
117
+
65
118
  override get debugInfo(): Record<string, unknown> {
66
119
  return {
67
120
  ...super.debugInfo,
@@ -1,13 +1,19 @@
1
1
  import {
2
+ createSymbol,
2
3
  Namekey,
3
4
  OutputSpace,
4
5
  OutputSymbol,
5
6
  OutputSymbolOptions,
6
- createSymbol,
7
+ track,
8
+ TrackOpTypes,
9
+ trigger,
10
+ TriggerOpTypes,
7
11
  } from "@alloy-js/core";
8
12
 
9
13
  export interface PythonOutputSymbolOptions extends OutputSymbolOptions {
10
14
  module?: string;
15
+ /** Whether this symbol is only used in type annotation contexts */
16
+ typeOnly?: boolean;
11
17
  }
12
18
 
13
19
  export interface CreatePythonSymbolFunctionOptions
@@ -28,6 +34,7 @@ export class PythonOutputSymbol extends OutputSymbol {
28
34
  ) {
29
35
  super(name, spaces, options);
30
36
  this.#module = options.module ?? undefined;
37
+ this.#typeOnly = options.typeOnly ?? false;
31
38
  }
32
39
 
33
40
  // The module in which the symbol is defined
@@ -37,6 +44,29 @@ export class PythonOutputSymbol extends OutputSymbol {
37
44
  return this.#module;
38
45
  }
39
46
 
47
+ #typeOnly: boolean;
48
+
49
+ /**
50
+ * Returns true if this symbol is only used in type annotation contexts.
51
+ * Such symbols can be imported inside a TYPE_CHECKING block.
52
+ */
53
+ get isTypeOnly() {
54
+ track(this, TrackOpTypes.GET, "typeOnly");
55
+ return this.#typeOnly;
56
+ }
57
+
58
+ /**
59
+ * Mark this symbol as also being used as a value (not just a type).
60
+ */
61
+ markAsValue() {
62
+ if (!this.#typeOnly) {
63
+ return;
64
+ }
65
+ const oldValue = this.#typeOnly;
66
+ this.#typeOnly = false;
67
+ trigger(this, TriggerOpTypes.SET, "typeOnly", false, oldValue);
68
+ }
69
+
40
70
  get staticMembers() {
41
71
  return this.memberSpaceFor("static")!;
42
72
  }
@@ -69,6 +99,7 @@ export class PythonOutputSymbol extends OutputSymbol {
69
99
  aliasTarget: this.aliasTarget,
70
100
  module: this.module,
71
101
  metadata: this.metadata,
102
+ typeOnly: this.isTypeOnly,
72
103
  });
73
104
 
74
105
  this.initializeCopy(copy);
@@ -13,8 +13,17 @@ import { PythonModuleScope } from "./python-module-scope.js";
13
13
  import { PythonOutputSymbol } from "./python-output-symbol.js";
14
14
  import { PythonOutputScope } from "./scopes.js";
15
15
 
16
+ export interface RefOptions {
17
+ /**
18
+ * If true, this reference is only used in a type annotation context.
19
+ * The import will be guarded with `if TYPE_CHECKING:`.
20
+ */
21
+ type?: boolean;
22
+ }
23
+
16
24
  export function ref(
17
25
  refkey: Refkey,
26
+ options?: RefOptions,
18
27
  ): () => [Children, PythonOutputSymbol | undefined] {
19
28
  const sourceFile = useContext(PythonSourceFileContext);
20
29
  const resolveResult = resolve<PythonOutputScope, PythonOutputSymbol>(
@@ -41,6 +50,7 @@ export function ref(
41
50
  sourceFile!.scope.addImport(
42
51
  lexicalDeclaration,
43
52
  pathDown[0] as PythonModuleScope,
53
+ { type: options?.type },
44
54
  ),
45
55
  );
46
56
  }