diff --git a/scripts/build/upToDate.js b/scripts/build/upToDate.js index f92fd6b503a..c3abb5d1013 100644 --- a/scripts/build/upToDate.js +++ b/scripts/build/upToDate.js @@ -4,7 +4,7 @@ const fs = require("fs"); const log = require("fancy-log"); // was `require("gulp-util").log (see https://github.com/gulpjs/gulp-util) const ts = require("../../lib/typescript"); const { Duplex } = require("stream"); -const chalk = require("./chalk"); +const chalk = /**@type {*} */(require("chalk")); const Vinyl = require("vinyl"); /** @@ -14,7 +14,7 @@ const Vinyl = require("vinyl"); * @param {UpToDateOptions} [options] * * @typedef UpToDateOptions - * @property {boolean} [verbose] + * @property {boolean | "minimal"} [verbose] * @property {(configFilePath: string) => ParsedCommandLine | undefined} [parseProject] */ function upToDate(parsedProject, options) { @@ -47,9 +47,9 @@ function upToDate(parsedProject, options) { cb(); }, final(cb) { - const status = ts.getUpToDateStatus(upToDateHost, parsedProject); + const status = getUpToDateStatus(upToDateHost, parsedProject); reportStatus(parsedProject, status, options); - if (status.type !== ts.UpToDateStatusType.UpToDate) { + if (status.type !== UpToDateStatusType.UpToDate) { for (const input of inputs) duplex.push(input); } duplex.push(null); @@ -88,11 +88,25 @@ function formatMessage(message, ...args) { /** * @param {ParsedCommandLine} project * @param {UpToDateStatus} status - * @param {{verbose?: boolean}} options + * @param {{verbose?: boolean | "minimal"}} options */ function reportStatus(project, status, options) { + switch (options.verbose) { + case "minimal": + switch (status.type) { + case UpToDateStatusType.UpToDate: + log.info(`Project '${fileName(project.options.configFilePath)}' is up to date.`); + break; + default: + log.info(`Project '${fileName(project.options.configFilePath)}' is out of date, rebuilding...`); + break; + } + break; + case true: + /**@type {*}*/(ts).formatUpToDateStatus(project.options.configFilePath, status, fileName, formatMessage); + break; + } if (!options.verbose) return; - ts.formatUpToDateStatus(project.options.configFilePath, status, fileName, formatMessage); } /** @@ -120,12 +134,302 @@ function formatStringFromArgs(text, args, baseIndex = 0) { return text.replace(/{(\d+)}/g, (_match, index) => args[+index + baseIndex]); } +const minimumDate = new Date(-8640000000000000); +const maximumDate = new Date(8640000000000000); +const missingFileModifiedTime = new Date(0); + +/** + * @typedef {0} UpToDateStatusType.Unbuildable + * @typedef {1} UpToDateStatusType.UpToDate + * @typedef {2} UpToDateStatusType.UpToDateWithUpstreamTypes + * @typedef {3} UpToDateStatusType.OutputMissing + * @typedef {4} UpToDateStatusType.OutOfDateWithSelf + * @typedef {5} UpToDateStatusType.OutOfDateWithUpstream + * @typedef {6} UpToDateStatusType.UpstreamOutOfDate + * @typedef {7} UpToDateStatusType.UpstreamBlocked + * @typedef {8} UpToDateStatusType.ComputingUpstream + * @typedef {9} UpToDateStatusType.ContainerOnly + * @enum {UpToDateStatusType.Unbuildable | UpToDateStatusType.UpToDate | UpToDateStatusType.UpToDateWithUpstreamTypes | UpToDateStatusType.OutputMissing | UpToDateStatusType.OutOfDateWithSelf | UpToDateStatusType.OutOfDateWithUpstream | UpToDateStatusType.UpstreamOutOfDate | UpToDateStatusType.UpstreamBlocked | UpToDateStatusType.ComputingUpstream | UpToDateStatusType.ContainerOnly} + */ +const UpToDateStatusType = { + Unbuildable: /** @type {0} */(0), + UpToDate: /** @type {1} */(1), + UpToDateWithUpstreamTypes: /** @type {2} */(2), + OutputMissing: /** @type {3} */(3), + OutOfDateWithSelf: /** @type {4} */(4), + OutOfDateWithUpstream: /** @type {5} */(5), + UpstreamOutOfDate: /** @type {6} */(6), + UpstreamBlocked: /** @type {7} */(7), + ComputingUpstream: /** @type {8} */(8), + ContainerOnly: /** @type {9} */(9), +}; + +/** + * @param {Date} date1 + * @param {Date} date2 + * @returns {Date} + */ +function newer(date1, date2) { + return date2 > date1 ? date2 : date1; +} + +/** + * @param {UpToDateHost} host + * @param {ParsedCommandLine | undefined} project + * @returns {UpToDateStatus} + */ +function getUpToDateStatus(host, project) { + if (project === undefined) return { type: UpToDateStatusType.Unbuildable, reason: "File deleted mid-build" }; + const prior = host.getLastStatus ? host.getLastStatus(project.options.configFilePath) : undefined; + if (prior !== undefined) { + return prior; + } + const actual = getUpToDateStatusWorker(host, project); + if (host.setLastStatus) { + host.setLastStatus(project.options.configFilePath, actual); + } + return actual; +} + +/** + * @param {UpToDateHost} host + * @param {ParsedCommandLine | undefined} project + * @returns {UpToDateStatus} + */ +function getUpToDateStatusWorker(host, project) { + /** @type {string} */ + let newestInputFileName = undefined; + let newestInputFileTime = minimumDate; + // Get timestamps of input files + for (const inputFile of project.fileNames) { + if (!host.fileExists(inputFile)) { + return { + type: UpToDateStatusType.Unbuildable, + reason: `${inputFile} does not exist` + }; + } + + const inputTime = host.getModifiedTime(inputFile) || missingFileModifiedTime; + if (inputTime > newestInputFileTime) { + newestInputFileName = inputFile; + newestInputFileTime = inputTime; + } + } + + // Collect the expected outputs of this project + const outputs = /**@type {string[]}*/(/**@type {*}*/(ts).getAllProjectOutputs(project)); + + if (outputs.length === 0) { + return { + type: UpToDateStatusType.ContainerOnly + }; + } + + // Now see if all outputs are newer than the newest input + let oldestOutputFileName = "(none)"; + let oldestOutputFileTime = maximumDate; + let newestOutputFileName = "(none)"; + let newestOutputFileTime = minimumDate; + /** @type {string | undefined} */ + let missingOutputFileName; + let newestDeclarationFileContentChangedTime = minimumDate; + let isOutOfDateWithInputs = false; + for (const output of outputs) { + // Output is missing; can stop checking + // Don't immediately return because we can still be upstream-blocked, which is a higher-priority status + if (!host.fileExists(output)) { + missingOutputFileName = output; + break; + } + + const outputTime = host.getModifiedTime(output) || missingFileModifiedTime; + if (outputTime < oldestOutputFileTime) { + oldestOutputFileTime = outputTime; + oldestOutputFileName = output; + } + + // If an output is older than the newest input, we can stop checking + // Don't immediately return because we can still be upstream-blocked, which is a higher-priority status + if (outputTime < newestInputFileTime) { + isOutOfDateWithInputs = true; + break; + } + + if (outputTime > newestOutputFileTime) { + newestOutputFileTime = outputTime; + newestOutputFileName = output; + } + + // Keep track of when the most recent time a .d.ts file was changed. + // In addition to file timestamps, we also keep track of when a .d.ts file + // had its file touched but not had its contents changed - this allows us + // to skip a downstream typecheck + if (path.extname(output) === ".d.ts") { + const unchangedTime = host.getUnchangedTime ? host.getUnchangedTime(output) : undefined; + if (unchangedTime !== undefined) { + newestDeclarationFileContentChangedTime = newer(unchangedTime, newestDeclarationFileContentChangedTime); + } + else { + const outputModifiedTime = host.getModifiedTime(output) || missingFileModifiedTime; + newestDeclarationFileContentChangedTime = newer(newestDeclarationFileContentChangedTime, outputModifiedTime); + } + } + } + + let pseudoUpToDate = false; + let usesPrepend = false; + /** @type {string | undefined} */ + let upstreamChangedProject; + if (project.projectReferences) { + if (host.setLastStatus) host.setLastStatus(project.options.configFilePath, { type: UpToDateStatusType.ComputingUpstream }); + for (const ref of project.projectReferences) { + usesPrepend = usesPrepend || !!(ref.prepend); + const resolvedRef = ts.resolveProjectReferencePath(host, ref); + const parsedRef = host.parseConfigFile ? host.parseConfigFile(resolvedRef) : ts.getParsedCommandLineOfConfigFile(resolvedRef, {}, parseConfigHost); + const refStatus = getUpToDateStatus(host, parsedRef); + + // Its a circular reference ignore the status of this project + if (refStatus.type === UpToDateStatusType.ComputingUpstream) { + continue; + } + + // An upstream project is blocked + if (refStatus.type === UpToDateStatusType.Unbuildable) { + return { + type: UpToDateStatusType.UpstreamBlocked, + upstreamProjectName: ref.path + }; + } + + // If the upstream project is out of date, then so are we (someone shouldn't have asked, though?) + if (refStatus.type !== UpToDateStatusType.UpToDate) { + return { + type: UpToDateStatusType.UpstreamOutOfDate, + upstreamProjectName: ref.path + }; + } + + // If the upstream project's newest file is older than our oldest output, we + // can't be out of date because of it + if (refStatus.newestInputFileTime && refStatus.newestInputFileTime <= oldestOutputFileTime) { + continue; + } + + // If the upstream project has only change .d.ts files, and we've built + // *after* those files, then we're "psuedo up to date" and eligible for a fast rebuild + if (refStatus.newestDeclarationFileContentChangedTime && refStatus.newestDeclarationFileContentChangedTime <= oldestOutputFileTime) { + pseudoUpToDate = true; + upstreamChangedProject = ref.path; + continue; + } + + // We have an output older than an upstream output - we are out of date + return { + type: UpToDateStatusType.OutOfDateWithUpstream, + outOfDateOutputFileName: oldestOutputFileName, + newerProjectName: ref.path + }; + } + } + + if (missingOutputFileName !== undefined) { + return { + type: UpToDateStatusType.OutputMissing, + missingOutputFileName + }; + } + + if (isOutOfDateWithInputs) { + return { + type: UpToDateStatusType.OutOfDateWithSelf, + outOfDateOutputFileName: oldestOutputFileName, + newerInputFileName: newestInputFileName + }; + } + + if (usesPrepend && pseudoUpToDate) { + return { + type: UpToDateStatusType.OutOfDateWithUpstream, + outOfDateOutputFileName: oldestOutputFileName, + newerProjectName: upstreamChangedProject + }; + } + + // Up to date + return { + type: pseudoUpToDate ? UpToDateStatusType.UpToDateWithUpstreamTypes : UpToDateStatusType.UpToDate, + newestDeclarationFileContentChangedTime, + newestInputFileTime, + newestOutputFileTime, + newestInputFileName, + newestOutputFileName, + oldestOutputFileName + }; +} + +const parseConfigHost = { + useCaseSensitiveFileNames: true, + getCurrentDirectory: () => process.cwd(), + readDirectory: (file) => fs.readdirSync(file), + fileExists: file => fs.existsSync(file) && fs.statSync(file).isFile(), + readFile: file => fs.readFileSync(file, "utf8"), + onUnRecoverableConfigFileDiagnostic: () => undefined +}; + /** * @typedef {import("vinyl")} File * @typedef {import("../../lib/typescript").ParsedCommandLine & { options: CompilerOptions }} ParsedCommandLine * @typedef {import("../../lib/typescript").CompilerOptions & { configFilePath?: string }} CompilerOptions - * @typedef {import("../../lib/typescript").UpToDateHost} UpToDateHost - * @typedef {import("../../lib/typescript").UpToDateStatus} UpToDateStatus * @typedef {import("../../lib/typescript").DiagnosticMessage} DiagnosticMessage + * @typedef UpToDateHost + * @property {(fileName: string) => boolean} fileExists + * @property {(fileName: string) => Date} getModifiedTime + * @property {(fileName: string) => Date} [getUnchangedTime] + * @property {(configFilePath: string) => ParsedCommandLine | undefined} parseConfigFile + * @property {(configFilePath: string) => UpToDateStatus} [getLastStatus] + * @property {(configFilePath: string, status: UpToDateStatus) => void} [setLastStatus] + * + * @typedef Status.Unbuildable + * @property {UpToDateStatusType.Unbuildable} type + * @property {string} reason + * + * @typedef Status.ContainerOnly + * @property {UpToDateStatusType.ContainerOnly} type + * + * @typedef Status.UpToDate + * @property {UpToDateStatusType.UpToDate | UpToDateStatusType.UpToDateWithUpstreamTypes} type + * @property {Date} [newestInputFileTime] + * @property {string} [newestInputFileName] + * @property {Date} [newestDeclarationFileContentChangedTime] + * @property {Date} [newestOutputFileTime] + * @property {string} [newestOutputFileName] + * @property {string} [oldestOutputFileName] + * + * @typedef Status.OutputMissing + * @property {UpToDateStatusType.OutputMissing} type + * @property {string} missingOutputFileName + * + * @typedef Status.OutOfDateWithSelf + * @property {UpToDateStatusType.OutOfDateWithSelf} type + * @property {string} outOfDateOutputFileName + * @property {string} newerInputFileName + * + * @typedef Status.UpstreamOutOfDate + * @property {UpToDateStatusType.UpstreamOutOfDate} type + * @property {string} upstreamProjectName + * + * @typedef Status.UpstreamBlocked + * @property {UpToDateStatusType.UpstreamBlocked} type + * @property {string} upstreamProjectName + * + * @typedef Status.ComputingUpstream + * @property {UpToDateStatusType.ComputingUpstream} type + * + * @typedef Status.OutOfDateWithUpstream + * @property {UpToDateStatusType.OutOfDateWithUpstream} type + * @property {string} outOfDateOutputFileName + * @property {string} newerProjectName + + * @typedef {Status.Unbuildable | Status.ContainerOnly | Status.UpToDate | Status.OutputMissing | Status.OutOfDateWithSelf | Status.UpstreamOutOfDate | Status.UpstreamBlocked | Status.ComputingUpstream | Status.OutOfDateWithUpstream} UpToDateStatus */ void 0; \ No newline at end of file diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 38d6f558242..59ff0f7c79f 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -3010,7 +3010,7 @@ namespace ts { transformFlags |= TransformFlags.AssertTypeScript; } - if (subtreeFlags & TransformFlags.ContainsSpread + if (subtreeFlags & TransformFlags.ContainsRestOrSpread || (expression.transformFlags & (TransformFlags.Super | TransformFlags.ContainsSuper))) { // If the this node contains a SpreadExpression, or is a super call, then it is an ES6 // node. @@ -3041,7 +3041,7 @@ namespace ts { if (node.typeArguments) { transformFlags |= TransformFlags.AssertTypeScript; } - if (subtreeFlags & TransformFlags.ContainsSpread) { + if (subtreeFlags & TransformFlags.ContainsRestOrSpread) { // If the this node contains a SpreadElementExpression then it is an ES6 // node. transformFlags |= TransformFlags.AssertES2015; @@ -3084,18 +3084,18 @@ namespace ts { // syntax. if (node.questionToken || node.type - || subtreeFlags & TransformFlags.ContainsDecorators + || (subtreeFlags & TransformFlags.ContainsTypeScriptClassSyntax && some(node.decorators)) || isThisIdentifier(name)) { transformFlags |= TransformFlags.AssertTypeScript; } // If a parameter has an accessibility modifier, then it is TypeScript syntax. if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) { - transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsParameterPropertyAssignments; + transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsTypeScriptClassSyntax; } // parameters with object rest destructuring are ES Next syntax - if (subtreeFlags & TransformFlags.ContainsObjectRest) { + if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { transformFlags |= TransformFlags.AssertESNext; } @@ -3148,7 +3148,7 @@ namespace ts { // TypeScript syntax. // An exported declaration may be TypeScript syntax, but is handled by the visitor // for a namespace declaration. - if ((subtreeFlags & TransformFlags.TypeScriptClassSyntaxMask) + if ((subtreeFlags & TransformFlags.ContainsTypeScriptClassSyntax) || node.typeParameters) { transformFlags |= TransformFlags.AssertTypeScript; } @@ -3170,7 +3170,7 @@ namespace ts { // A class with a parameter property assignment, property initializer, or decorator is // TypeScript syntax. - if (subtreeFlags & TransformFlags.TypeScriptClassSyntaxMask + if (subtreeFlags & TransformFlags.ContainsTypeScriptClassSyntax || node.typeParameters) { transformFlags |= TransformFlags.AssertTypeScript; } @@ -3247,7 +3247,7 @@ namespace ts { } // function declarations with object rest destructuring are ES Next syntax - if (subtreeFlags & TransformFlags.ContainsObjectRest) { + if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { transformFlags |= TransformFlags.AssertESNext; } @@ -3271,7 +3271,7 @@ namespace ts { } // function declarations with object rest destructuring are ES Next syntax - if (subtreeFlags & TransformFlags.ContainsObjectRest) { + if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { transformFlags |= TransformFlags.AssertESNext; } @@ -3302,7 +3302,7 @@ namespace ts { } // function declarations with object rest destructuring are ES Next syntax - if (subtreeFlags & TransformFlags.ContainsObjectRest) { + if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { transformFlags |= TransformFlags.AssertESNext; } @@ -3317,7 +3317,7 @@ namespace ts { // If the PropertyDeclaration has an initializer or a computed name, we need to inform its ancestor // so that it handle the transformation. if (node.initializer || isComputedPropertyName(node.name)) { - transformFlags |= TransformFlags.ContainsPropertyInitializer; + transformFlags |= TransformFlags.ContainsTypeScriptClassSyntax; } node.transformFlags = transformFlags | TransformFlags.HasComputedFlags; @@ -3351,7 +3351,7 @@ namespace ts { } // function declarations with object rest destructuring are ES Next syntax - if (subtreeFlags & TransformFlags.ContainsObjectRest) { + if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { transformFlags |= TransformFlags.AssertESNext; } @@ -3393,7 +3393,7 @@ namespace ts { } // function expressions with object rest destructuring are ES Next syntax - if (subtreeFlags & TransformFlags.ContainsObjectRest) { + if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { transformFlags |= TransformFlags.AssertESNext; } @@ -3434,7 +3434,7 @@ namespace ts { } // arrow functions with object rest destructuring are ES Next syntax - if (subtreeFlags & TransformFlags.ContainsObjectRest) { + if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { transformFlags |= TransformFlags.AssertESNext; } @@ -3482,7 +3482,7 @@ namespace ts { transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern; // A VariableDeclaration containing ObjectRest is ESNext syntax - if (subtreeFlags & TransformFlags.ContainsObjectRest) { + if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { transformFlags |= TransformFlags.AssertESNext; } @@ -3731,11 +3731,11 @@ namespace ts { break; case SyntaxKind.SpreadElement: - transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsSpread; + transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsRestOrSpread; break; case SyntaxKind.SpreadAssignment: - transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsObjectSpread; + transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsObjectRestOrSpread; break; case SyntaxKind.SuperKeyword: @@ -3751,8 +3751,8 @@ namespace ts { case SyntaxKind.ObjectBindingPattern: transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsBindingPattern; - if (subtreeFlags & TransformFlags.ContainsRest) { - transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsObjectRest; + if (subtreeFlags & TransformFlags.ContainsRestOrSpread) { + transformFlags |= TransformFlags.AssertESNext | TransformFlags.ContainsObjectRestOrSpread; } excludeFlags = TransformFlags.BindingPatternExcludes; break; @@ -3765,13 +3765,13 @@ namespace ts { case SyntaxKind.BindingElement: transformFlags |= TransformFlags.AssertES2015; if ((node).dotDotDotToken) { - transformFlags |= TransformFlags.ContainsRest; + transformFlags |= TransformFlags.ContainsRestOrSpread; } break; case SyntaxKind.Decorator: // This node is TypeScript syntax, and marks its container as also being TypeScript syntax. - transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsDecorators; + transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsTypeScriptClassSyntax; break; case SyntaxKind.ObjectLiteralExpression: @@ -3788,7 +3788,7 @@ namespace ts { transformFlags |= TransformFlags.ContainsLexicalThis; } - if (subtreeFlags & TransformFlags.ContainsObjectSpread) { + if (subtreeFlags & TransformFlags.ContainsObjectRestOrSpread) { // If an ObjectLiteralExpression contains a spread element, then it // is an ES next node. transformFlags |= TransformFlags.AssertESNext; @@ -3799,7 +3799,7 @@ namespace ts { case SyntaxKind.ArrayLiteralExpression: case SyntaxKind.NewExpression: excludeFlags = TransformFlags.ArrayLiteralOrCallOrNewExcludes; - if (subtreeFlags & TransformFlags.ContainsSpread) { + if (subtreeFlags & TransformFlags.ContainsRestOrSpread) { // If the this node contains a SpreadExpression, then it is an ES6 // node. transformFlags |= TransformFlags.AssertES2015; diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 14d1659dc76..cde5ee290a3 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -307,8 +307,8 @@ namespace ts { if (!getRestIndicatorOfBindingOrAssignmentElement(element)) { const propertyName = getPropertyNameOfBindingOrAssignmentElement(element)!; if (flattenContext.level >= FlattenLevel.ObjectRest - && !(element.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) - && !(getTargetOfBindingOrAssignmentElement(element)!.transformFlags & (TransformFlags.ContainsRest | TransformFlags.ContainsObjectRest)) + && !(element.transformFlags & (TransformFlags.ContainsRestOrSpread | TransformFlags.ContainsObjectRestOrSpread)) + && !(getTargetOfBindingOrAssignmentElement(element)!.transformFlags & (TransformFlags.ContainsRestOrSpread | TransformFlags.ContainsObjectRestOrSpread)) && !isComputedPropertyName(propertyName)) { bindingElements = append(bindingElements, element); } @@ -384,7 +384,7 @@ namespace ts { if (flattenContext.level >= FlattenLevel.ObjectRest) { // If an array pattern contains an ObjectRest, we must cache the result so that we // can perform the ObjectRest destructuring in a different declaration - if (element.transformFlags & TransformFlags.ContainsObjectRest) { + if (element.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { const temp = createTempVariable(/*recordTempVariable*/ undefined); if (flattenContext.hoistTempVariables) { flattenContext.context.hoistVariableDeclaration(temp); diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 2e5176c4490..154aee0442c 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -3729,7 +3729,7 @@ namespace ts { function visitCallExpressionWithPotentialCapturedThisAssignment(node: CallExpression, assignToCapturedThis: boolean): CallExpression | BinaryExpression { // We are here either because SuperKeyword was used somewhere in the expression, or // because we contain a SpreadElementExpression. - if (node.transformFlags & TransformFlags.ContainsSpread || + if (node.transformFlags & TransformFlags.ContainsRestOrSpread || node.expression.kind === SyntaxKind.SuperKeyword || isSuperProperty(skipOuterExpressions(node.expression))) { @@ -3739,7 +3739,7 @@ namespace ts { } let resultingCall: CallExpression | BinaryExpression; - if (node.transformFlags & TransformFlags.ContainsSpread) { + if (node.transformFlags & TransformFlags.ContainsRestOrSpread) { // [source] // f(...a, b) // x.m(...a, b) @@ -3802,7 +3802,7 @@ namespace ts { * @param node A NewExpression node. */ function visitNewExpression(node: NewExpression): LeftHandSideExpression { - if (node.transformFlags & TransformFlags.ContainsSpread) { + if (node.transformFlags & TransformFlags.ContainsRestOrSpread) { // We are here because we contain a SpreadElementExpression. // [source] // new C(...a) diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index b121eb315db..6f0db492653 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -210,7 +210,7 @@ namespace ts { } function visitObjectLiteralExpression(node: ObjectLiteralExpression): Expression { - if (node.transformFlags & TransformFlags.ContainsObjectSpread) { + if (node.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { // spread elements emit like so: // non-spread elements are chunked together into object literals, and then all are passed to __assign: // { a, ...o, b } => __assign({a}, o, {b}); @@ -250,7 +250,7 @@ namespace ts { * @param node A BinaryExpression node. */ function visitBinaryExpression(node: BinaryExpression, noDestructuringValue: boolean): Expression { - if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsObjectRest) { + if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { return flattenDestructuringAssignment( node, visitor, @@ -276,7 +276,7 @@ namespace ts { */ function visitVariableDeclaration(node: VariableDeclaration): VisitResult { // If we are here it is because the name contains a binding pattern with a rest somewhere in it. - if (isBindingPattern(node.name) && node.name.transformFlags & TransformFlags.ContainsObjectRest) { + if (isBindingPattern(node.name) && node.name.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { return flattenDestructuringBinding( node, visitor, @@ -307,7 +307,7 @@ namespace ts { * @param node A ForOfStatement. */ function visitForOfStatement(node: ForOfStatement, outermostLabeledStatement: LabeledStatement | undefined): VisitResult { - if (node.initializer.transformFlags & TransformFlags.ContainsObjectRest) { + if (node.initializer.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { node = transformForOfStatementWithObjectRest(node); } if (node.awaitModifier) { @@ -499,7 +499,7 @@ namespace ts { } function visitParameter(node: ParameterDeclaration): ParameterDeclaration { - if (node.transformFlags & TransformFlags.ContainsObjectRest) { + if (node.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { // Binding patterns are converted into a generated name and are // evaluated inside the function body. return updateParameter( @@ -716,7 +716,7 @@ namespace ts { function appendObjectRestAssignmentsIfNeeded(statements: Statement[] | undefined, node: FunctionLikeDeclaration): Statement[] | undefined { for (const parameter of node.parameters) { - if (parameter.transformFlags & TransformFlags.ContainsObjectRest) { + if (parameter.transformFlags & TransformFlags.ContainsObjectRestOrSpread) { const temp = getGeneratedNameForNode(parameter); const declarations = flattenDestructuringBinding( parameter, diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 7af0b7c5ad7..a5ab1e8475f 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -973,9 +973,11 @@ namespace ts { // Check if we have property assignment inside class declaration. // If there is a property assignment, we need to emit constructor whether users define it or not // If there is no property assignment, we can omit constructor if users do not define it - const hasInstancePropertyWithInitializer = forEach(node.members, isInstanceInitializedProperty); - const hasParameterPropertyAssignments = node.transformFlags & TransformFlags.ContainsParameterPropertyAssignments; const constructor = getFirstConstructorWithBody(node); + const hasInstancePropertyWithInitializer = forEach(node.members, isInstanceInitializedProperty); + const hasParameterPropertyAssignments = constructor && + constructor.transformFlags & TransformFlags.ContainsTypeScriptClassSyntax && + forEach(constructor.parameters, isParameterWithPropertyAssignment); // If the class does not contain nodes that require a synthesized constructor, // accept the current constructor if it exists. diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9b1c57b2097..087f09a65df 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4908,25 +4908,21 @@ namespace ts { // Markers // - Flags used to indicate that a subtree contains a specific transformation. - ContainsDecorators = 1 << 12, - ContainsPropertyInitializer = 1 << 13, - ContainsLexicalThis = 1 << 14, - ContainsCapturedLexicalThis = 1 << 15, - ContainsLexicalThisInComputedPropertyName = 1 << 16, - ContainsDefaultValueAssignments = 1 << 17, - ContainsParameterPropertyAssignments = 1 << 18, - ContainsSpread = 1 << 19, - ContainsObjectSpread = 1 << 20, - ContainsRest = ContainsSpread, - ContainsObjectRest = ContainsObjectSpread, - ContainsComputedPropertyName = 1 << 21, - ContainsBlockScopedBinding = 1 << 22, - ContainsBindingPattern = 1 << 23, - ContainsYield = 1 << 24, - ContainsHoistedDeclarationOrCompletion = 1 << 25, - ContainsDynamicImport = 1 << 26, - Super = 1 << 27, - ContainsSuper = 1 << 28, + ContainsTypeScriptClassSyntax = 1 << 12, // Decorators, Property Initializers, Parameter Property Initializers + ContainsLexicalThis = 1 << 13, + ContainsCapturedLexicalThis = 1 << 14, + ContainsLexicalThisInComputedPropertyName = 1 << 15, + ContainsDefaultValueAssignments = 1 << 16, + ContainsRestOrSpread = 1 << 17, + ContainsObjectRestOrSpread = 1 << 18, + ContainsComputedPropertyName = 1 << 19, + ContainsBlockScopedBinding = 1 << 20, + ContainsBindingPattern = 1 << 21, + ContainsYield = 1 << 22, + ContainsHoistedDeclarationOrCompletion = 1 << 23, + ContainsDynamicImport = 1 << 24, + Super = 1 << 25, + ContainsSuper = 1 << 26, // Please leave this as 1 << 29. // It is the maximum bit we can set before we outgrow the size of a v8 small integer (SMI) on an x86 system. @@ -4950,23 +4946,22 @@ namespace ts { OuterExpressionExcludes = TypeScript | ES2015 | DestructuringAssignment | Generator | HasComputedFlags, PropertyAccessExcludes = OuterExpressionExcludes | Super, NodeExcludes = PropertyAccessExcludes | ContainsSuper, - ArrowFunctionExcludes = NodeExcludes | ContainsDecorators | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRest, - FunctionExcludes = NodeExcludes | ContainsDecorators | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRest, - ConstructorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRest, - MethodOrAccessorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRest, - ClassExcludes = NodeExcludes | ContainsDecorators | ContainsPropertyInitializer | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsComputedPropertyName | ContainsParameterPropertyAssignments | ContainsLexicalThisInComputedPropertyName, - ModuleExcludes = NodeExcludes | ContainsDecorators | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsHoistedDeclarationOrCompletion, + ArrowFunctionExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread, + FunctionExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread, + ConstructorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread, + MethodOrAccessorExcludes = NodeExcludes | ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread, + ClassExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsComputedPropertyName | ContainsLexicalThisInComputedPropertyName, + ModuleExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding | ContainsHoistedDeclarationOrCompletion, TypeExcludes = ~ContainsTypeScript, - ObjectLiteralExcludes = NodeExcludes | ContainsDecorators | ContainsComputedPropertyName | ContainsLexicalThisInComputedPropertyName | ContainsObjectSpread, - ArrayLiteralOrCallOrNewExcludes = NodeExcludes | ContainsSpread, - VariableDeclarationListExcludes = NodeExcludes | ContainsBindingPattern | ContainsObjectRest, + ObjectLiteralExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsComputedPropertyName | ContainsLexicalThisInComputedPropertyName | ContainsObjectRestOrSpread, + ArrayLiteralOrCallOrNewExcludes = NodeExcludes | ContainsRestOrSpread, + VariableDeclarationListExcludes = NodeExcludes | ContainsBindingPattern | ContainsObjectRestOrSpread, ParameterExcludes = NodeExcludes, - CatchClauseExcludes = NodeExcludes | ContainsObjectRest, - BindingPatternExcludes = NodeExcludes | ContainsRest, + CatchClauseExcludes = NodeExcludes | ContainsObjectRestOrSpread, + BindingPatternExcludes = NodeExcludes | ContainsRestOrSpread, // Masks // - Additional bitmasks - TypeScriptClassSyntaxMask = ContainsParameterPropertyAssignments | ContainsPropertyInitializer | ContainsDecorators, ES2015FunctionSyntaxMask = ContainsCapturedLexicalThis | ContainsDefaultValueAssignments, }