diff --git a/packages/transformers/typescript-types/src/collect.js b/packages/transformers/typescript-types/src/collect.js index ac3bf7fb7fe..be7d920169d 100644 --- a/packages/transformers/typescript-types/src/collect.js +++ b/packages/transformers/typescript-types/src/collect.js @@ -11,13 +11,16 @@ export function collect( context: any, sourceFile: any, ): any { + // Factory only exists on TS >= 4.0 + const {factory = ts} = context; + // When module definitions are nested inside each other (e.g with module augmentation), // we want to keep track of the hierarchy so we can associated nodes with the right module. const moduleStack: Array = []; let _currentModule: ?TSModule; let visit = (node: any): any => { if (ts.isBundle(node)) { - return ts.updateBundle(node, ts.visitNodes(node.sourceFiles, visit)); + return factory.updateBundle(node, ts.visitNodes(node.sourceFiles, visit)); } if (ts.isModuleDeclaration(node)) { @@ -85,12 +88,12 @@ export function collect( currentModule.addExport('default', node.expression.text); } - if (isDeclaration(ts, node)) { + if (isDeclaration(node)) { if (node.name) { currentModule.addLocal(node.name.text, node); } - let name = getExportedName(ts, node); + let name = getExportedName(node); if (name) { currentModule.addLocal(name, node); currentModule.addExport(name, name); diff --git a/packages/transformers/typescript-types/src/shake.js b/packages/transformers/typescript-types/src/shake.js index 571b1158b97..0e056f3b669 100644 --- a/packages/transformers/typescript-types/src/shake.js +++ b/packages/transformers/typescript-types/src/shake.js @@ -4,13 +4,22 @@ import type {TSModuleGraph} from './TSModuleGraph'; import ts from 'typescript'; import nullthrows from 'nullthrows'; -import {getExportedName, isDeclaration, createImportSpecifier} from './utils'; +import {getExportedName, isDeclaration} from './utils'; +import { + createImportClause, + createImportDeclaration, + createImportSpecifier, + updateExportDeclaration, +} from './wrappers'; export function shake( moduleGraph: TSModuleGraph, context: any, sourceFile: any, ): any { + // Factory only exists on TS >= 4.0 + const {factory = ts} = context; + // We traverse things out of order which messes with typescript's internal state. // We don't rely on the lexical environment, so just overwrite with noops to avoid errors. context.suspendLexicalEnvironment = () => {}; @@ -28,7 +37,7 @@ export function shake( let _currentModule: ?TSModule; let visit = (node: any): any => { if (ts.isBundle(node)) { - return ts.updateBundle(node, ts.visitNodes(node.sourceFiles, visit)); + return factory.updateBundle(node, ts.visitNodes(node.sourceFiles, visit)); } // Flatten all module declarations into the top-level scope @@ -43,7 +52,7 @@ export function shake( node.modifiers.splice( index, 0, - ts.createModifier(ts.SyntaxKind.DeclareKeyword), + factory.createModifier(ts.SyntaxKind.DeclareKeyword), ); return node; } @@ -55,7 +64,7 @@ export function shake( _currentModule = moduleStack.pop(); if (isFirstModule && !addedGeneratedImports) { - statements.unshift(...generateImports(moduleGraph)); + statements.unshift(...generateImports(factory, moduleGraph)); addedGeneratedImports = true; } @@ -92,12 +101,14 @@ export function shake( } if (exported.length > 0) { - return ts.updateExportDeclaration( + return updateExportDeclaration( + factory, node, - undefined, // decorators undefined, // modifiers - ts.updateNamedExports(node.exportClause, exported), + false, // isTypeOnly + factory.updateNamedExports(node.exportClause, exported), undefined, // moduleSpecifier + undefined, // assertClause ); } } @@ -114,8 +125,8 @@ export function shake( } } - if (isDeclaration(ts, node)) { - let name = getExportedName(ts, node) || node.name.text; + if (isDeclaration(node)) { + let name = getExportedName(node) || node.name.text; // Remove unused declarations if (!currentModule.used.has(name)) { @@ -123,7 +134,6 @@ export function shake( } // Remove original export modifiers - node = ts.getMutableClone(node); node.modifiers = (node.modifiers || []).filter( m => m.kind !== ts.SyntaxKind.ExportKeyword && @@ -133,23 +143,27 @@ export function shake( // Rename declarations let newName = currentModule.getName(name); if (newName !== name && newName !== 'default') { - node.name = ts.createIdentifier(newName); + node.name = factory.createIdentifier(newName); } // Export declarations that should be exported if (exportedNames.get(newName) === currentModule) { if (newName === 'default') { node.modifiers.unshift( - ts.createModifier(ts.SyntaxKind.DefaultKeyword), + factory.createModifier(ts.SyntaxKind.DefaultKeyword), ); } - node.modifiers.unshift(ts.createModifier(ts.SyntaxKind.ExportKeyword)); + node.modifiers.unshift( + factory.createModifier(ts.SyntaxKind.ExportKeyword), + ); } else if ( ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) ) { - node.modifiers.unshift(ts.createModifier(ts.SyntaxKind.DeclareKeyword)); + node.modifiers.unshift( + factory.createModifier(ts.SyntaxKind.DeclareKeyword), + ); } } @@ -173,10 +187,14 @@ export function shake( d => exportedNames.get(d.name.text) === currentModule, ); if (isExported) { - node.modifiers.unshift(ts.createModifier(ts.SyntaxKind.ExportKeyword)); + node.modifiers.unshift( + factory.createModifier(ts.SyntaxKind.ExportKeyword), + ); } else { // Otherwise, add `declare` modifier (required for top-level declarations in d.ts files). - node.modifiers.unshift(ts.createModifier(ts.SyntaxKind.DeclareKeyword)); + node.modifiers.unshift( + factory.createModifier(ts.SyntaxKind.DeclareKeyword), + ); } return node; @@ -193,7 +211,7 @@ export function shake( if (ts.isIdentifier(node) && currentModule.names.has(node.text)) { let newName = nullthrows(currentModule.getName(node.text)); if (newName !== 'default') { - return ts.createIdentifier(newName); + return factory.createIdentifier(newName); } } @@ -205,11 +223,11 @@ export function shake( node.right.text, ); if (resolved && resolved.module.hasBinding(resolved.name)) { - return ts.createIdentifier(resolved.name); + return factory.createIdentifier(resolved.name); } else { - return ts.updateQualifiedName( + return factory.updateQualifiedName( node, - ts.createIdentifier(currentModule.getName(node.left.text)), + factory.createIdentifier(currentModule.getName(node.left.text)), node.right, ); } @@ -231,7 +249,7 @@ export function shake( return ts.visitNode(sourceFile, visit); } -function generateImports(moduleGraph: TSModuleGraph) { +function generateImports(factory: any, moduleGraph: TSModuleGraph) { let importStatements = []; for (let [specifier, names] of moduleGraph.getAllImports()) { let defaultSpecifier; @@ -239,53 +257,58 @@ function generateImports(moduleGraph: TSModuleGraph) { let namedSpecifiers = []; for (let [name, imported] of names) { if (imported === 'default') { - defaultSpecifier = ts.createIdentifier(name); + defaultSpecifier = factory.createIdentifier(name); } else if (imported === '*') { - namespaceSpecifier = ts.createNamespaceImport( - ts.createIdentifier(name), + namespaceSpecifier = factory.createNamespaceImport( + factory.createIdentifier(name), ); } else { namedSpecifiers.push( createImportSpecifier( - ts, - name === imported ? undefined : ts.createIdentifier(imported), - ts.createIdentifier(name), + factory, + false, + name === imported ? undefined : factory.createIdentifier(imported), + factory.createIdentifier(name), ), ); } } if (namespaceSpecifier) { - let importClause = ts.createImportClause( + let importClause = createImportClause( + factory, + false, defaultSpecifier, namespaceSpecifier, ); importStatements.push( - ts.createImportDeclaration( - undefined, + createImportDeclaration( + factory, undefined, importClause, - // $FlowFixMe - ts.createLiteral(specifier), + factory.createStringLiteral(specifier), + undefined, ), ); defaultSpecifier = undefined; } if (defaultSpecifier || namedSpecifiers.length > 0) { - let importClause = ts.createImportClause( + let importClause = createImportClause( + factory, + false, defaultSpecifier, namedSpecifiers.length > 0 - ? ts.createNamedImports(namedSpecifiers) + ? factory.createNamedImports(namedSpecifiers) : undefined, ); importStatements.push( - ts.createImportDeclaration( - undefined, + createImportDeclaration( + factory, undefined, importClause, - // $FlowFixMe - ts.createLiteral(specifier), + factory.createStringLiteral(specifier), + undefined, ), ); } diff --git a/packages/transformers/typescript-types/src/utils.js b/packages/transformers/typescript-types/src/utils.js index 326f9f9dabb..ac6e38887a3 100644 --- a/packages/transformers/typescript-types/src/utils.js +++ b/packages/transformers/typescript-types/src/utils.js @@ -1,8 +1,7 @@ // @flow -import typeof TypeScriptModule from 'typescript'; // eslint-disable-line import/no-extraneous-dependencies -import type {Identifier, ImportSpecifier} from 'typescript'; +import ts from 'typescript'; -export function getExportedName(ts: TypeScriptModule, node: any): ?string { +export function getExportedName(node: any): ?string { if (!node.modifiers) { return null; } @@ -18,7 +17,7 @@ export function getExportedName(ts: TypeScriptModule, node: any): ?string { return node.name.text; } -export function isDeclaration(ts: TypeScriptModule, node: any): boolean { +export function isDeclaration(node: any): boolean { return ( ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node) || @@ -27,22 +26,3 @@ export function isDeclaration(ts: TypeScriptModule, node: any): boolean { ts.isTypeAliasDeclaration(node) ); } - -export function createImportSpecifier( - ts: TypeScriptModule, - propertyName: Identifier | void, - name: Identifier, - isTypeOnly: boolean = false, -): ImportSpecifier { - const [majorVersion, minorVersion] = ts.versionMajorMinor - .split('.') - .map(num => parseInt(num, 10)); - // The signature of createImportSpecifier had a breaking change in Typescript 4.5. - // see: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-5.html#type-modifiers-on-import-names - if (majorVersion > 4 || (majorVersion === 4 && minorVersion >= 5)) { - // $FlowFixMe - return ts.createImportSpecifier(isTypeOnly, propertyName, name); - } else { - return ts.createImportSpecifier(propertyName, name); - } -} diff --git a/packages/transformers/typescript-types/src/wrappers.js b/packages/transformers/typescript-types/src/wrappers.js new file mode 100644 index 00000000000..2a61e86eecb --- /dev/null +++ b/packages/transformers/typescript-types/src/wrappers.js @@ -0,0 +1,202 @@ +// @flow +/* eslint-disable no-unused-vars */ +import type { + ExportDeclaration, + Expression, + Identifier, + ImportClause, + ImportDeclaration, + ImportSpecifier, + Modifier, + NamedImportBindings, +} from 'typescript'; + +import ts from 'typescript'; +import invariant from 'assert'; + +type AssertClause = any; +type NamedExportBindings = any; + +const [majorVersion, minorVersion] = ts.versionMajorMinor + .split('.') + .map(num => parseInt(num, 10)); + +// Everything below was generated using https://github.com/mischnic/tsc-version-wrapper + +export const createImportClause: ( + factory: any, + isTypeOnly: boolean, + name: Identifier | void, + namedBindings: NamedImportBindings | void, +) => ImportClause = (() => { + if (majorVersion > 4 || (majorVersion === 4 && minorVersion >= 0)) { + return (factory, isTypeOnly, name, namedBindings) => + factory.createImportClause(isTypeOnly, name, namedBindings); + } else if (majorVersion > 3 || (majorVersion === 3 && minorVersion >= 8)) { + return (factory, isTypeOnly, name, namedBindings) => + factory.createImportClause(name, namedBindings, isTypeOnly); + } else if (majorVersion > 3 || (majorVersion === 3 && minorVersion >= 0)) { + return (factory, isTypeOnly, name, namedBindings) => + factory.createImportClause(name, namedBindings); + } else { + invariant(false); + } +})(); + +export const createImportDeclaration: ( + factory: any, + modifiers: Modifier[] | void, + importClause: ImportClause | void, + moduleSpecifier: Expression, + assertClause: AssertClause, +) => ImportDeclaration = (() => { + if (majorVersion > 4 || (majorVersion === 4 && minorVersion >= 8)) { + return (factory, modifiers, importClause, moduleSpecifier, assertClause) => + factory.createImportDeclaration( + modifiers, + importClause, + moduleSpecifier, + assertClause, + ); + } else if (majorVersion > 4 || (majorVersion === 4 && minorVersion >= 5)) { + return (factory, modifiers, importClause, moduleSpecifier, assertClause) => + factory.createImportDeclaration( + undefined /* decorators */, + modifiers, + importClause, + moduleSpecifier, + assertClause, + ); + } else if (majorVersion > 3 || (majorVersion === 3 && minorVersion >= 0)) { + return (factory, modifiers, importClause, moduleSpecifier, assertClause) => + factory.createImportDeclaration( + undefined /* decorators */, + modifiers, + importClause, + moduleSpecifier, + ); + } else { + invariant(false); + } +})(); + +export const createImportSpecifier: ( + factory: any, + isTypeOnly: boolean, + propertyName: Identifier | void, + name: Identifier, +) => ImportSpecifier = (() => { + if (majorVersion > 4 || (majorVersion === 4 && minorVersion >= 5)) { + return (factory, isTypeOnly, propertyName, name) => + factory.createImportSpecifier(isTypeOnly, propertyName, name); + } else if (majorVersion > 3 || (majorVersion === 3 && minorVersion >= 0)) { + return (factory, isTypeOnly, propertyName, name) => + factory.createImportSpecifier(propertyName, name); + } else { + invariant(false); + } +})(); + +export const updateExportDeclaration: ( + factory: any, + node: ExportDeclaration, + modifiers: Modifier[] | void, + isTypeOnly: boolean, + exportClause: NamedExportBindings | void, + moduleSpecifier: Expression | void, + assertClause: AssertClause | void, +) => ExportDeclaration = (() => { + if (majorVersion > 4 || (majorVersion === 4 && minorVersion >= 8)) { + return ( + factory, + node, + modifiers, + isTypeOnly, + exportClause, + moduleSpecifier, + assertClause, + ) => + factory.updateExportDeclaration( + node, + modifiers, + isTypeOnly, + exportClause, + moduleSpecifier, + assertClause, + ); + } else if (majorVersion > 4 || (majorVersion === 4 && minorVersion >= 5)) { + return ( + factory, + node, + modifiers, + isTypeOnly, + exportClause, + moduleSpecifier, + assertClause, + ) => + factory.updateExportDeclaration( + node, + undefined /* decorators */, + modifiers, + isTypeOnly, + exportClause, + moduleSpecifier, + assertClause, + ); + } else if (majorVersion > 4 || (majorVersion === 4 && minorVersion >= 0)) { + return ( + factory, + node, + modifiers, + isTypeOnly, + exportClause, + moduleSpecifier, + assertClause, + ) => + factory.updateExportDeclaration( + node, + undefined /* decorators */, + modifiers, + isTypeOnly, + exportClause, + moduleSpecifier, + ); + } else if (majorVersion > 3 || (majorVersion === 3 && minorVersion >= 8)) { + return ( + factory, + node, + modifiers, + isTypeOnly, + exportClause, + moduleSpecifier, + assertClause, + ) => + factory.updateExportDeclaration( + node, + undefined /* decorators */, + modifiers, + exportClause, + moduleSpecifier, + isTypeOnly, + ); + } else if (majorVersion > 3 || (majorVersion === 3 && minorVersion >= 0)) { + return ( + factory, + node, + modifiers, + isTypeOnly, + exportClause, + moduleSpecifier, + assertClause, + ) => + factory.updateExportDeclaration( + node, + undefined /* decorators */, + modifiers, + exportClause, + moduleSpecifier, + ); + } else { + invariant(false); + } +})(); diff --git a/packages/utils/parcel-lsp/package.json b/packages/utils/parcel-lsp/package.json index 18be3181102..9af846640b9 100644 --- a/packages/utils/parcel-lsp/package.json +++ b/packages/utils/parcel-lsp/package.json @@ -50,6 +50,6 @@ "@typescript-eslint/parser": "^4.14.1", "eslint": "^7.19.0", "glob": "^7.1.6", - "typescript": "^4.6.4" + "typescript": ">=3.0.0" } } diff --git a/packages/utils/parcelforvscode/package.json b/packages/utils/parcelforvscode/package.json index 4498b12e6ca..07ccd717b29 100644 --- a/packages/utils/parcelforvscode/package.json +++ b/packages/utils/parcelforvscode/package.json @@ -33,7 +33,7 @@ "eslint": "^7.19.0", "glob": "^7.1.6", "mocha": "^8.2.1", - "typescript": "^4.6.4", + "typescript": ">=3.0.0", "vscode-test": "^1.5.0" }, "dependencies": { diff --git a/yarn.lock b/yarn.lock index 8fad4cfb22b..2ce40a3cebe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3716,9 +3716,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001272, caniuse-lite@^1.0.30001286: - version "1.0.30001349" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001349.tgz#90740086a2eb2e825084944169d313c9793aeba4" - integrity sha512-VFaWW3jeo6DLU5rwdiasosxhYSduJgSGil4cSyX3/85fbctlE58pXAkWyuRmVA0r2RxsOSVYUTZcySJ8WpbTxw== + version "1.0.30001434" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz#ec1ec1cfb0a93a34a0600d37903853030520a4e5" + integrity sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA== caseless@~0.12.0: version "0.12.0" @@ -6192,7 +6192,7 @@ flatted@^3.1.0: flow-bin@0.184.0: version "0.184.0" - resolved "https://packages.atlassian.com/api/npm/npm-remote/flow-bin/-/flow-bin-0.184.0.tgz#0256b3c302ce465b60d0f0296273840d38d3f9e6" + resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.184.0.tgz#0256b3c302ce465b60d0f0296273840d38d3f9e6" integrity sha512-HiHuxhO06dqhV7YabluSswm3ZgxVi2L+aArcuIJMON/CRzqkGQrRjIVNbKllMs95rFk6aeuFR3FdVCCUa0SbGw== flush-write-stream@^1.0.0, flush-write-stream@^1.0.2: @@ -12985,10 +12985,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@>=3.0.0, typescript@^4.6.4: - version "4.6.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.4.tgz#caa78bbc3a59e6a5c510d35703f6a09877ce45e9" - integrity sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg== +typescript@>=3.0.0: + version "4.8.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.2.tgz#e3b33d5ccfb5914e4eeab6699cf208adee3fd790" + integrity sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw== uglify-js@^3.1.4: version "3.7.6"