From aeaba41664dfb45320dc0cacbc17cfc07e9b09a6 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Mon, 11 Mar 2024 15:02:23 -0400 Subject: [PATCH] Handle dynamic import type references in TS types transformer (#9573) --- packages/core/integration-tests/package.json | 1 + .../core/integration-tests/test/ts-types.js | 66 +++++++++++++++++++ .../typescript-types/src/TSModuleGraph.js | 2 + .../typescript-types/src/collect.js | 23 ++++++- yarn.lock | 24 +++++++ 5 files changed, 114 insertions(+), 2 deletions(-) diff --git a/packages/core/integration-tests/package.json b/packages/core/integration-tests/package.json index 45884d51844..a6e1ed2f1cc 100644 --- a/packages/core/integration-tests/package.json +++ b/packages/core/integration-tests/package.json @@ -21,6 +21,7 @@ "@babel/preset-typescript": "^7.22.11", "@jetbrains/kotlinc-js-api": "^1.2.12", "@mdx-js/react": "^1.5.3", + "@types/react": "^17", "autoprefixer": "^10.4.0", "chalk": "^4.1.0", "command-exists": "^1.2.6", diff --git a/packages/core/integration-tests/test/ts-types.js b/packages/core/integration-tests/test/ts-types.js index 3ba82f906a5..c09d989dc9e 100644 --- a/packages/core/integration-tests/test/ts-types.js +++ b/packages/core/integration-tests/test/ts-types.js @@ -7,6 +7,7 @@ import { overlayFS, outputFS, ncp, + fsFixture, } from '@parcel/test-utils'; import {md} from '@parcel/diagnostic'; import {normalizeSeparators} from '@parcel/utils'; @@ -450,4 +451,69 @@ describe('typescript types', function () { ); assert.equal(dist, expected); }); + + it('should handle dynamic imports generated by TS', async function () { + let dir = __dirname + '/dynamic-import-ts'; + await overlayFS.mkdirp(dir); + await fsFixture(overlayFS, dir)` + yarn.lock: + + package.json: + { + "types": "dist/types.d.ts" + } + + index.ts: + export * from "./ErrorBoundary"; + export * from "./ErrorBoundaryContext"; + + foo.js: + import {baz} from './baz'; + export function foo() { + return 'foo' + baz(); + } + + ErrorBoundaryContext.ts: + import { createContext } from "react"; + export type ErrorBoundaryContextType = {}; + export const ErrorBoundaryContext = createContext(null); + + ErrorBoundary.ts: + import { Component, createElement, PropsWithChildren } from "react"; + import { ErrorBoundaryContext } from "./ErrorBoundaryContext"; + + export class ErrorBoundary extends Component { + render() { + const { children } = this.props; + + return createElement( + ErrorBoundaryContext.Provider, + { + value: {}, + }, + children + ); + } + } + `; + + let b = await bundle(path.join(dir, '/index.ts'), { + inputFS: overlayFS, + mode: 'production', + }); + + let output = await outputFS.readFile(b.getBundles()[0].filePath, 'utf8'); + assert.equal( + output, + `import { Context, Component, PropsWithChildren, ProviderProps, FunctionComponentElement } from "react"; +export type ErrorBoundaryContextType = {}; +export const ErrorBoundaryContext: Context; +export class ErrorBoundary extends Component { + render(): FunctionComponentElement>; +} + +//# sourceMappingURL=types.d.ts.map +`, + ); + }); }); diff --git a/packages/transformers/typescript-types/src/TSModuleGraph.js b/packages/transformers/typescript-types/src/TSModuleGraph.js index a7dc12600ed..bfeb9fba65e 100644 --- a/packages/transformers/typescript-types/src/TSModuleGraph.js +++ b/packages/transformers/typescript-types/src/TSModuleGraph.js @@ -9,11 +9,13 @@ export class TSModuleGraph { modules: Map; mainModuleName: string; mainModule: ?TSModule; + syntheticImportCount: number; constructor(mainModuleName: string) { this.modules = new Map(); this.mainModuleName = mainModuleName; this.mainModule = null; + this.syntheticImportCount = 0; } addModule(name: string, module: TSModule) { diff --git a/packages/transformers/typescript-types/src/collect.js b/packages/transformers/typescript-types/src/collect.js index be7d920169d..eddf82f3338 100644 --- a/packages/transformers/typescript-types/src/collect.js +++ b/packages/transformers/typescript-types/src/collect.js @@ -83,6 +83,26 @@ export function collect( } } + node = ts.visitEachChild(node, visit, context); + + if ( + ts.isImportTypeNode(node) && + ts.isLiteralTypeNode(node.argument) && + ts.isStringLiteral(node.argument.literal) + ) { + let local = `$$parcel$import$${moduleGraph.syntheticImportCount++}`; + if (node.qualifier) { + currentModule.addImport( + local, + node.argument.literal.text, + node.qualifier.text, + ); + } else { + currentModule.addImport(local, node.argument.literal.text, '*'); + } + return factory.createTypeReferenceNode(local, node.typeArguments); + } + // Handle `export default name;` if (ts.isExportAssignment(node) && ts.isIdentifier(node.expression)) { currentModule.addExport('default', node.expression.text); @@ -112,13 +132,12 @@ export function collect( } } - const results = ts.visitEachChild(node, visit, context); // After we finish traversing the children of a module definition, // we need to make sure that subsequent nodes get associated with the next-highest level module. if (ts.isModuleDeclaration(node)) { _currentModule = moduleStack.pop(); } - return results; + return node; }; return ts.visitNode(sourceFile, visit); diff --git a/yarn.lock b/yarn.lock index df2b5b5d151..bcb3c34364b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2641,6 +2641,25 @@ resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== +"@types/prop-types@*": + version "15.7.11" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563" + integrity sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng== + +"@types/react@^17": + version "17.0.76" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.76.tgz#bfdf762046699e265655cd8f67a51beab6cd1e80" + integrity sha512-w9Aq+qeszGYoQM0hgFcdsAODGJdogadBDiitPm+zjBFJ0mLymvn2qSXsDaLJUndFRqqXk1FQfa9avHUBk1JhJQ== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/scheduler@*": + version "0.16.8" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" + integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== + "@types/semver@^7.3.12": version "7.5.0" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" @@ -5037,6 +5056,11 @@ csstype@^2.6.8: resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.11.tgz#452f4d024149ecf260a852b025e36562a253ffc5" integrity sha512-l8YyEC9NBkSm783PFTvh0FmJy7s5pFKrDp49ZL7zBGX3fWkO+N4EEyan1qqp8cwPLDcD0OSdyY6hAMoxp34JFw== +csstype@^3.0.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + csstype@^3.0.6: version "3.1.2" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"