From 2c197a2bd6e5ff6846acc3df116598a3f7a5522e Mon Sep 17 00:00:00 2001 From: Ben Lickly Date: Fri, 17 Nov 2023 17:12:00 -0800 Subject: [PATCH] Upgrade TypeScript to 5.3.2. PiperOrigin-RevId: 583523736 --- src/googmodule.ts | 33 ++++-- src/jsdoc_transformer.ts | 7 +- src/ns_transformer.ts | 49 ++++++++- src/summary.ts | 1 + src/transformer_util.ts | 16 +-- src/ts_migration_exports_shim.ts | 3 + test/googmodule_test.ts | 26 ++--- test/test_support.ts | 15 +++ test/tsickle_test.ts | 103 +++++++++++++------ test_files/comments/trailing_no_semicolon.js | 22 ++++ test_files/comments/trailing_no_semicolon.ts | 17 +++ test_files/decl_merge/outer_enum.js | 31 ++++++ test_files/decl_merge/outer_enum.ts | 20 ++++ test_files/decl_merge/rejected_ns.js | 42 +++++--- test_files/decl_merge/rejected_ns.ts | 15 +-- test_files/enum/enum.js | 3 +- test_files/enum/enum.ts | 1 + 17 files changed, 305 insertions(+), 99 deletions(-) create mode 100644 test_files/comments/trailing_no_semicolon.js create mode 100644 test_files/comments/trailing_no_semicolon.ts create mode 100644 test_files/decl_merge/outer_enum.js create mode 100644 test_files/decl_merge/outer_enum.ts diff --git a/src/googmodule.ts b/src/googmodule.ts index bf6e6d91b..56c239a02 100644 --- a/src/googmodule.ts +++ b/src/googmodule.ts @@ -29,11 +29,9 @@ export interface GoogModuleProcessorHost { * Takes the import URL of an ES6 import and returns the googmodule module * name for the imported module, iff the module is an original closure * JavaScript file. - * - * Warning: If this function is present, GoogModule won't produce diagnostics - * for multiple provides. */ - jsPathToModuleName?(importPath: string): string|undefined; + jsPathToModuleName? + (importPath: string): {name: string, multipleProvides: boolean}|undefined; /** * Takes the import URL of an ES6 import and returns the property name that * should be stripped from the usage. @@ -89,7 +87,8 @@ export function jsPathToNamespace( host: GoogModuleProcessorHost, context: ts.Node, diagnostics: ts.Diagnostic[], importPath: string, getModuleSymbol: () => ts.Symbol | undefined): string|undefined { - const namespace = localJsPathToNamespace(host, importPath); + const namespace = + localJsPathToNamespace(host, context, diagnostics, importPath); if (namespace) return namespace; const moduleSymbol = getModuleSymbol(); @@ -105,7 +104,8 @@ export function jsPathToNamespace( * Forwards to `jsPathToModuleName` on the host if present. */ export function localJsPathToNamespace( - host: GoogModuleProcessorHost, importPath: string): string|undefined { + host: GoogModuleProcessorHost, context: ts.Node|undefined, + diagnostics: ts.Diagnostic[], importPath: string): string|undefined { if (importPath.match(/^goog:/)) { // This is a namespace import, of the form "goog:foo.bar". // Fix it to just "foo.bar". @@ -113,7 +113,12 @@ export function localJsPathToNamespace( } if (host.jsPathToModuleName) { - return host.jsPathToModuleName(importPath); + const module = host.jsPathToModuleName(importPath); + if (!module) return undefined; + if (module.multipleProvides) { + reportMultipleProvidesError(context, diagnostics, importPath); + } + return module.name; } return undefined; @@ -394,10 +399,7 @@ function getGoogNamespaceFromClutzComments( findLocalInDeclarations(moduleSymbol, '__clutz_multiple_provides'); if (hasMultipleProvides) { // Report an error... - reportDiagnostic( - tsickleDiagnostics, context, - `referenced JavaScript module ${ - tsImport} provides multiple namespaces and cannot be imported by path.`); + reportMultipleProvidesError(context, tsickleDiagnostics, tsImport); // ... but continue producing an emit that effectively references the first // provided symbol (to continue finding any additional errors). } @@ -411,6 +413,15 @@ function getGoogNamespaceFromClutzComments( return actualNamespace; } +function reportMultipleProvidesError( + context: ts.Node|undefined, diagnostics: ts.Diagnostic[], + importPath: string) { + reportDiagnostic( + diagnostics, context, + `referenced JavaScript module ${ + importPath} provides multiple namespaces and cannot be imported by path.`); +} + /** * Converts a TS/ES module './import/path' into a goog.module compatible * namespace, handling regular imports and `goog:` namespace imports. diff --git a/src/jsdoc_transformer.ts b/src/jsdoc_transformer.ts index 6e3e5119b..cdcc55945 100644 --- a/src/jsdoc_transformer.ts +++ b/src/jsdoc_transformer.ts @@ -846,10 +846,9 @@ export function jsdocTransformer( continue; } } - const newDecl = - // TODO: go/ts50upgrade - Remove after upgrade. - // tslint:disable-next-line:no-unnecessary-type-assertion - ts.visitNode(decl, visitor, ts.isVariableDeclaration)!; + const newDecl = ts.setEmitFlags( + ts.visitNode(decl, visitor, ts.isVariableDeclaration)!, + ts.EmitFlags.NoComments); const newStmt = ts.factory.createVariableStatement( varStmt.modifiers, ts.factory.createVariableDeclarationList([newDecl], flags)); diff --git a/src/ns_transformer.ts b/src/ns_transformer.ts index 67300c942..129c15a3d 100644 --- a/src/ns_transformer.ts +++ b/src/ns_transformer.ts @@ -72,8 +72,8 @@ export function namespaceTransformer( // transformation fails. function transformNamespace( ns: ts.ModuleDeclaration, - mergedDecl: ts.ClassDeclaration| - ts.InterfaceDeclaration): ts.Statement[] { + mergedDecl: ts.ClassDeclaration|ts.InterfaceDeclaration| + ts.EnumDeclaration): ts.Statement[] { if (!ns.body || !ts.isModuleBlock(ns.body)) { if (ts.isModuleDeclaration(ns)) { error( @@ -83,10 +83,15 @@ export function namespaceTransformer( return [ns]; } const nsName = getIdentifierText(ns.name as ts.Identifier); + const mergingWithEnum = ts.isEnumDeclaration(mergedDecl); const transformedNsStmts: ts.Statement[] = []; for (const stmt of ns.body.statements) { if (ts.isEmptyStatement(stmt)) continue; if (ts.isClassDeclaration(stmt)) { + if (mergingWithEnum) { + errorNotAllowed(stmt, 'class'); + continue; + } transformInnerDeclaration( stmt, (classDecl, notExported, hoistedIdent) => { return ts.factory.updateClassDeclaration( @@ -95,12 +100,20 @@ export function namespaceTransformer( classDecl.members); }); } else if (ts.isEnumDeclaration(stmt)) { + if (mergingWithEnum) { + errorNotAllowed(stmt, 'enum'); + continue; + } transformInnerDeclaration( stmt, (enumDecl, notExported, hoistedIdent) => { return ts.factory.updateEnumDeclaration( enumDecl, notExported, hoistedIdent, enumDecl.members); }); } else if (ts.isInterfaceDeclaration(stmt)) { + if (mergingWithEnum) { + errorNotAllowed(stmt, 'interface'); + continue; + } transformInnerDeclaration( stmt, (interfDecl, notExported, hoistedIdent) => { return ts.factory.updateInterfaceDeclaration( @@ -109,6 +122,10 @@ export function namespaceTransformer( interfDecl.members); }); } else if (ts.isTypeAliasDeclaration(stmt)) { + if (mergingWithEnum) { + errorNotAllowed(stmt, 'type alias'); + continue; + } transformTypeAliasDeclaration(stmt); } else if (ts.isVariableStatement(stmt)) { if ((ts.getCombinedNodeFlags(stmt.declarationList) & @@ -116,13 +133,28 @@ export function namespaceTransformer( error( stmt, 'non-const values are not supported. (go/ts-merged-namespaces)'); + continue; } if (!ts.isInterfaceDeclaration(mergedDecl)) { error( stmt, 'const declaration only allowed when merging with an interface (go/ts-merged-namespaces)'); + continue; } transformConstDeclaration(stmt); + } else if (ts.isFunctionDeclaration(stmt)) { + if (!ts.isEnumDeclaration(mergedDecl)) { + error( + stmt, + 'function declaration only allowed when merging with an enum (go/ts-merged-namespaces)'); + } + transformInnerDeclaration( + stmt, (funcDecl, notExported, hoistedIdent) => { + return ts.factory.updateFunctionDeclaration( + funcDecl, notExported, funcDecl.asteriskToken, + hoistedIdent, funcDecl.typeParameters, + funcDecl.parameters, funcDecl.type, funcDecl.body); + }); } else { error( stmt, @@ -145,6 +177,12 @@ export function namespaceTransformer( // Local functions follow. + function errorNotAllowed(stmt: ts.Statement, declKind: string) { + error( + stmt, + `${declKind} cannot be merged with enum declaration. (go/ts-merged-namespaces)`); + } + type DeclarationStatement = ts.Declaration&ts.DeclarationStatement; function transformConstDeclaration(varDecl: ts.VariableStatement) { @@ -365,12 +403,13 @@ export function namespaceTransformer( } if (!ts.isInterfaceDeclaration(mergedDecl) && - !ts.isClassDeclaration(mergedDecl)) { - // The previous declaration is not a class or interface. + !ts.isClassDeclaration(mergedDecl) && + !ts.isEnumDeclaration(mergedDecl)) { + // The previous declaration is not a class, enum, or interface. transformedStmts.push(ns); // Nothing to do here. error( ns.name, - 'merged declaration must be local class or interface. (go/ts-merged-namespaces)'); + 'merged declaration must be local class, enum, or interface. (go/ts-merged-namespaces)'); return; } diff --git a/src/summary.ts b/src/summary.ts index ffbff10fb..9f2623345 100644 --- a/src/summary.ts +++ b/src/summary.ts @@ -54,6 +54,7 @@ export class FileSummary { modName: string|undefined; autochunk = false; enhanceable = false; + legacyNamespace = false; moduleType = ModuleType.UNKNOWN; private stringify(symbol: Symbol): string { diff --git a/src/transformer_util.ts b/src/transformer_util.ts index 6c60ef0dd..4722b81d9 100644 --- a/src/transformer_util.ts +++ b/src/transformer_util.ts @@ -227,28 +227,30 @@ export function reportDebugWarning( * @param textRange pass to overrride the text range from the node with a more specific range. */ export function reportDiagnostic( - diagnostics: ts.Diagnostic[], node: ts.Node, messageText: string, textRange?: ts.TextRange, - category = ts.DiagnosticCategory.Error) { + diagnostics: ts.Diagnostic[], node: ts.Node|undefined, messageText: string, + textRange?: ts.TextRange, category = ts.DiagnosticCategory.Error) { diagnostics.push(createDiagnostic(node, messageText, textRange, category)); } function createDiagnostic( - node: ts.Node, messageText: string, textRange: ts.TextRange|undefined, + node: ts.Node|undefined, messageText: string, + textRange: ts.TextRange|undefined, category: ts.DiagnosticCategory): ts.Diagnostic { - let start, length: number; + let start: number|undefined; + let length: number|undefined; // getStart on a synthesized node can crash (due to not finding an associated // source file). Make sure to use the original node. node = ts.getOriginalNode(node); if (textRange) { start = textRange.pos; length = textRange.end - textRange.pos; - } else { + } else if (node) { // Only use getStart if node has a valid pos, as it might be synthesized. start = node.pos >= 0 ? node.getStart() : 0; length = node.end - node.pos; } return { - file: node.getSourceFile(), + file: node?.getSourceFile(), start, length, messageText, @@ -431,4 +433,4 @@ export function getPreviousDeclaration( } } return null; -} \ No newline at end of file +} diff --git a/src/ts_migration_exports_shim.ts b/src/ts_migration_exports_shim.ts index ac715764b..0136ea45e 100644 --- a/src/ts_migration_exports_shim.ts +++ b/src/ts_migration_exports_shim.ts @@ -457,6 +457,9 @@ class Generator { fileSummary.addStrongRequire({type: Type.CLOSURE, name: 'goog'}); fileSummary.addStrongRequire( {type: Type.CLOSURE, name: this.srcIds.googModuleId}); + if (maybeDeclareLegacyNameCall) { + fileSummary.legacyNamespace = true; + } fileSummary.autochunk = isAutoChunk; fileSummary.moduleType = ModuleType.GOOG_MODULE; diff --git a/test/googmodule_test.ts b/test/googmodule_test.ts index a258a7bcf..4d804cbb5 100644 --- a/test/googmodule_test.ts +++ b/test/googmodule_test.ts @@ -13,6 +13,7 @@ import * as googmodule from '../src/googmodule'; import {ModulesManifest} from '../src/modules_manifest'; import * as testSupport from './test_support'; +import {outdent} from './test_support'; interface ResolvedNamespace { name: string; @@ -42,8 +43,14 @@ function processES5( transformDynamicImport: 'closure', }; if (pathToNamespaceMap) { - host.jsPathToModuleName = (importPath: string) => - pathToNamespaceMap.get(importPath)?.name; + host.jsPathToModuleName = (importPath: string) => { + const module = pathToNamespaceMap.get(importPath); + if (!module) return undefined; + return { + name: module.name, + multipleProvides: false, + }; + }; host.jsPathToStripProperty = (importPath: string) => pathToNamespaceMap.get(importPath)?.stripProperty; } @@ -71,21 +78,6 @@ function processES5( return {output, manifest, rootDir}; } -/** - * Remove the first line (if empty) and unindents the all other lines by the - * amount of leading whitespace in the second line. - */ -function outdent(str: string) { - const lines = str.split('\n'); - if (lines.length < 2) return str; - if (lines.shift() !== '') return str; - const indent = lines[0].match(/^ */)![0].length; - for (let i = 0; i < lines.length; i++) { - lines[i] = lines[i].substring(indent); - } - return lines.join('\n'); -} - describe('convertCommonJsToGoogModule', () => { beforeEach(() => { testSupport.addDiffMatchers(); diff --git a/test/test_support.ts b/test/test_support.ts index c270ac144..6f0f2b826 100644 --- a/test/test_support.ts +++ b/test/test_support.ts @@ -453,3 +453,18 @@ export function pathToModuleName( if (fileName === tslibPath()) return 'tslib'; return cliSupport.pathToModuleName(rootModulePath, context, fileName); } + +/** + * Remove the first line (if empty) and unindents the all other lines by the + * amount of leading whitespace in the second line. + */ +export function outdent(str: string) { + const lines = str.split('\n'); + if (lines.length < 2) return str; + if (lines.shift() !== '') return str; + const indent = lines[0].match(/^ */)![0].length; + for (let i = 0; i < lines.length; i++) { + lines[i] = lines[i].substring(indent); + } + return lines.join('\n'); +} diff --git a/test/tsickle_test.ts b/test/tsickle_test.ts index 7fbb9592a..11d59e3e7 100644 --- a/test/tsickle_test.ts +++ b/test/tsickle_test.ts @@ -13,14 +13,14 @@ import {assertAbsolute} from '../src/cli_support'; import * as tsickle from '../src/tsickle'; import * as testSupport from './test_support'; +import {outdent} from './test_support'; describe('emitWithTsickle', () => { function emitWithTsickle( tsSources: {[fileName: string]: string}, tsConfigOverride: Partial = {}, tsickleHostOverride: Partial = {}, - customTransformers?: tsickle.EmitTransformers): - {[fileName: string]: string} { + customTransformers?: tsickle.EmitTransformers) { const tsCompilerOptions: ts.CompilerOptions = { ...testSupport.compilerOptions, target: ts.ScriptTarget.ES5, @@ -55,13 +55,13 @@ describe('emitWithTsickle', () => { return importPath.replace(/\/|\\/g, '.'); }, fileNameToModuleId: (fileName) => fileName.replace(/^\.\//, ''), - ...tsickleHostOverride, options: tsCompilerOptions, rootDirsRelative: testSupport.relativeToTsickleRoot, - transformDynamicImport: 'closure' + transformDynamicImport: 'closure', + ...tsickleHostOverride, }; const jsSources: {[fileName: string]: string} = {}; - tsickle.emit( + const {diagnostics} = tsickle.emit( program, tsickleHost, (fileName: string, data: string) => { jsSources[path.relative(tsCompilerOptions.rootDir!, fileName)] = data; @@ -69,7 +69,7 @@ describe('emitWithTsickle', () => { /* sourceFile */ undefined, /* cancellationToken */ undefined, /* emitOnlyDtsFiles */ undefined, customTransformers); - return jsSources; + return {jsSources, diagnostics}; } @@ -91,7 +91,7 @@ describe('emitWithTsickle', () => { const tsSources = { 'a.ts': `export const x = 1;`, }; - const jsSources = emitWithTsickle( + const {jsSources} = emitWithTsickle( tsSources, undefined, { shouldSkipTsickleProcessing: () => true, }, @@ -106,12 +106,10 @@ describe('emitWithTsickle', () => { 'b.ts': `export * from './a';`, }; - const jsSources = emitWithTsickle( - tsSources, { - preserveConstEnums: true, - module: ts.ModuleKind.ES2015, - }, - {googmodule: false}); + const {jsSources} = emitWithTsickle(tsSources, { + preserveConstEnums: true, + module: ts.ModuleKind.ES2015, + }); expect(jsSources['b.js']).toContain(`export { Foo } from './a';`); }); @@ -121,16 +119,62 @@ describe('emitWithTsickle', () => { 'a.ts': `export function f() : typeof f { return f; }`, }; - const jsSources = emitWithTsickle(tsSources, { + const {jsSources} = emitWithTsickle(tsSources, { module: ts.ModuleKind.ES2015, }); - expect(jsSources['a.js']).toContain(` -/** - * @return {function(): ?} - */ -export function f() { return f; } -`); + expect(jsSources['a.js']).toContain(outdent(` + /** + * @return {function(): ?} + */ + export function f() { return f; } + `)); + }); + + it('reports multi-provides error with jsPathToModuleName impl', () => { + const tsSources = { + 'a.ts': `import {} from 'google3/multi/provide';`, + 'clutz.d.ts': `declare module 'google3/multi/provide' { export {}; }`, + }; + const {diagnostics} = + emitWithTsickle( + tsSources, /* tsConfigOverride= */ undefined, + /* tsickleHostOverride= */ { + jsPathToModuleName(importPath: string) { + if (importPath === 'google3/multi/provide') { + return { + name: 'multi.provide', + multipleProvides: true, + }; + } + return undefined; + } + }); + expect(testSupport.formatDiagnostics(diagnostics)) + .toContain( + 'referenced JavaScript module google3/multi/provide provides multiple namespaces and cannot be imported by path'); + }); + + it('allows side-effect import of multi-provides module', () => { + const tsSources = { + 'a.ts': `import 'google3/multi/provide';`, + 'clutz.d.ts': `declare module 'google3/multi/provide' { export {}; }`, + }; + const {jsSources} = emitWithTsickle( + tsSources, /* tsConfigOverride= */ undefined, + /* tsickleHostOverride= */ { + googmodule: true, + jsPathToModuleName(importPath: string) { + if (importPath === 'google3/multi/provide') { + return { + name: 'multi.provide', + multipleProvides: true, + }; + } + return undefined; + }, + }); + expect(jsSources['a.js']).toContain(`goog.require('multi.provide');`); }); describe('regressions', () => { @@ -140,16 +184,15 @@ export function f() { return f; } 'a.ts': `export const x = 1;`, 'b.ts': `export * from './a';\n`, }; - const jsSources = emitWithTsickle( - tsSources, { - declaration: true, - module: ts.ModuleKind.ES2015, - }, - {googmodule: false}); - - expect(jsSources['b.d.ts']) - .toEqual(`//!! generated by tsickle from b.ts -export * from './a';\n`); + const {jsSources} = emitWithTsickle(tsSources, { + declaration: true, + module: ts.ModuleKind.ES2015, + }); + + expect(jsSources['b.d.ts']).toEqual(outdent(` + //!! generated by tsickle from b.ts + export * from './a'; + `)); }); }); }); diff --git a/test_files/comments/trailing_no_semicolon.js b/test_files/comments/trailing_no_semicolon.js new file mode 100644 index 000000000..f16924b09 --- /dev/null +++ b/test_files/comments/trailing_no_semicolon.js @@ -0,0 +1,22 @@ +/** + * + * @fileoverview Tests that the JSDoc comment of `other` is only emitted once. + * Without the trailing semicolon after `noExplicitSemicolon` TypeScript seems + * to duplicate the trailing comment as soon as a custom transformer modifies + * the variable statement. + * + * Generated from: test_files/comments/trailing_no_semicolon.ts + */ +goog.module('test_files.comments.trailing_no_semicolon'); +var module = module || { id: 'test_files/comments/trailing_no_semicolon.ts' }; +goog.require('tslib'); +/** @type {number} */ +const noExplicitSemicolon = 0; +/** + * This is a comment with a JSDoc tag + * JSCompiler doesn't recognize + * + * \@foobar + * @type {number} + */ +exports.other = 1; diff --git a/test_files/comments/trailing_no_semicolon.ts b/test_files/comments/trailing_no_semicolon.ts new file mode 100644 index 000000000..b68b9844a --- /dev/null +++ b/test_files/comments/trailing_no_semicolon.ts @@ -0,0 +1,17 @@ +/** + * @fileoverview Tests that the JSDoc comment of `other` is only emitted once. + * Without the trailing semicolon after `noExplicitSemicolon` TypeScript seems + * to duplicate the trailing comment as soon as a custom transformer modifies + * the variable statement. + */ + + +const noExplicitSemicolon = 0 + +/** + * This is a comment with a JSDoc tag + * JSCompiler doesn't recognize + * + * @foobar + */ +export const other = 1; diff --git a/test_files/decl_merge/outer_enum.js b/test_files/decl_merge/outer_enum.js new file mode 100644 index 000000000..9f3827e1a --- /dev/null +++ b/test_files/decl_merge/outer_enum.js @@ -0,0 +1,31 @@ +/** + * + * @fileoverview Ensure that a function declared in a declaration + * merging namespace is generated as a property of the merged outer enum. + * + * Generated from: test_files/decl_merge/outer_enum.ts + * @suppress {uselessCode,checkTypes} + * + */ +goog.module('test_files.decl_merge.outer_enum'); +var module = module || { id: 'test_files/decl_merge/outer_enum.ts' }; +goog.require('tslib'); +/** @enum {number} */ +const E = { + a: 42, + b: 43, +}; +exports.E = E; +E[E.a] = 'a'; +E[E.b] = 'b'; +/** + * @param {string} s + * @return {!E} + */ +function E$fromString(s) { + return s === 'a' ? E.a : E.b; +} +/** @const */ +E.fromString = E$fromString; +/** @type {!E} */ +const e = E.fromString('a'); diff --git a/test_files/decl_merge/outer_enum.ts b/test_files/decl_merge/outer_enum.ts new file mode 100644 index 000000000..b89996cc3 --- /dev/null +++ b/test_files/decl_merge/outer_enum.ts @@ -0,0 +1,20 @@ +/** + * @fileoverview Ensure that a function declared in a declaration + * merging namespace is generated as a property of the merged outer enum. + * + * @suppress {uselessCode,checkTypes} + */ + +export enum E { + a = 42, + b +} + +// tslint:disable-next-line:no-namespace +export namespace E { + export function fromString(s: string) { + return s === 'a' ? E.a : E.b; + }; +} + +const e = E.fromString('a'); diff --git a/test_files/decl_merge/rejected_ns.js b/test_files/decl_merge/rejected_ns.js index 54aa1d565..2fbd3bfa2 100644 --- a/test_files/decl_merge/rejected_ns.js +++ b/test_files/decl_merge/rejected_ns.js @@ -1,12 +1,13 @@ -// test_files/decl_merge/rejected_ns.ts(34,1): warning TS0: type/symbol conflict for Inbetween, using {?} for now +// test_files/decl_merge/rejected_ns.ts(32,1): warning TS0: type/symbol conflict for Inbetween, using {?} for now // test_files/decl_merge/rejected_ns.ts(9,11): error TS0: transformation of plain namespace not supported. (go/ts-merged-namespaces) -// test_files/decl_merge/rejected_ns.ts(13,11): error TS0: merged declaration must be local class or interface. (go/ts-merged-namespaces) -// test_files/decl_merge/rejected_ns.ts(21,11): error TS0: merged declaration must be local class or interface. (go/ts-merged-namespaces) -// test_files/decl_merge/rejected_ns.ts(26,3): error TS0: const declaration only allowed when merging with an interface (go/ts-merged-namespaces) -// test_files/decl_merge/rejected_ns.ts(38,3): error TS0: non-const values are not supported. (go/ts-merged-namespaces) -// test_files/decl_merge/rejected_ns.ts(40,9): error TS0: 'K' must be exported. (go/ts-merged-namespaces) -// test_files/decl_merge/rejected_ns.ts(42,16): error TS0: Destructuring declarations are not supported. (go/ts-merged-namespaces) -// test_files/decl_merge/rejected_ns.ts(47,11): error TS0: nested namespaces are not supported. (go/ts-merged-namespaces) +// test_files/decl_merge/rejected_ns.ts(13,11): error TS0: merged declaration must be local class, enum, or interface. (go/ts-merged-namespaces) +// test_files/decl_merge/rejected_ns.ts(19,3): error TS0: const declaration only allowed when merging with an interface (go/ts-merged-namespaces) +// test_files/decl_merge/rejected_ns.ts(24,3): error TS0: function declaration only allowed when merging with an enum (go/ts-merged-namespaces) +// test_files/decl_merge/rejected_ns.ts(36,3): error TS0: non-const values are not supported. (go/ts-merged-namespaces) +// test_files/decl_merge/rejected_ns.ts(38,9): error TS0: 'K' must be exported. (go/ts-merged-namespaces) +// test_files/decl_merge/rejected_ns.ts(40,16): error TS0: Destructuring declarations are not supported. (go/ts-merged-namespaces) +// test_files/decl_merge/rejected_ns.ts(44,3): error TS0: function declaration only allowed when merging with an enum (go/ts-merged-namespaces) +// test_files/decl_merge/rejected_ns.ts(48,11): error TS0: nested namespaces are not supported. (go/ts-merged-namespaces) /** * * @fileoverview Test namespace transformations that are not supported @@ -24,21 +25,21 @@ goog.require('tslib'); * @return {void} */ function funcToBeMerged() { } -/** @enum {number} */ -const Colors = { - red: 0, - green: 1, - blue: 2, -}; -Colors[Colors.red] = 'red'; -Colors[Colors.green] = 'green'; -Colors[Colors.blue] = 'blue'; // Adding const values is only allowed on interfaces. class Cabbage { } (function (Cabbage) { Cabbage.C = 0; })(Cabbage || (Cabbage = {})); +// Adding functions is only allowed on enums. +(function (Cabbage) { + /** + * @return {void} + */ + function foo() { } + Cabbage.foo = foo; + ; +})(Cabbage || (Cabbage = {})); /** @type {{a: number, b: string}} */ const o = { a: 0, @@ -60,6 +61,13 @@ var Inbetween; // Destructuring declarations are not allowed. Inbetween.a = o.a, Inbetween.b = o.b; })(Inbetween || (Inbetween = {})); +(function (Inbetween) { + /** + * @return {void} + */ + function foo() { } + Inbetween.foo = foo; +})(Inbetween || (Inbetween = {})); // Nested namespaces are not supported. class A { } diff --git a/test_files/decl_merge/rejected_ns.ts b/test_files/decl_merge/rejected_ns.ts index 2e414d803..fdb0df004 100644 --- a/test_files/decl_merge/rejected_ns.ts +++ b/test_files/decl_merge/rejected_ns.ts @@ -12,13 +12,6 @@ namespace notMerging {} function funcToBeMerged() {} namespace funcToBeMerged {} -// Declaration merging with enums is not supported. -enum Colors { - red, - green, - blue -} -namespace Colors {} // Adding const values is only allowed on interfaces. class Cabbage {} @@ -26,6 +19,11 @@ namespace Cabbage { export const C = 0; } +// Adding functions is only allowed on enums. +namespace Cabbage { + export function foo() {}; +} + const o = { a: 0, b: '' @@ -42,6 +40,9 @@ namespace Inbetween { export const {a, b} = o; } +namespace Inbetween { + export function foo() {} +} // Nested namespaces are not supported. class A {} namespace A.B {} diff --git a/test_files/enum/enum.js b/test_files/enum/enum.js index 8041302d5..e67603054 100644 --- a/test_files/enum/enum.js +++ b/test_files/enum/enum.js @@ -57,7 +57,8 @@ let variableUsingExportedEnum; const ComponentIndex = { Scheme: 1, UserInfo: 2, - Domain: 0, + // TODO: b/313666408 - Fix tsc to not duplicate comments like the following + Domain: 0, // Be sure to exercise the code with a 0 enum value. // Be sure to exercise the code with a 0 enum value. UserInfo2: 2, }; diff --git a/test_files/enum/enum.ts b/test_files/enum/enum.ts index 8f913933b..b070ff424 100644 --- a/test_files/enum/enum.ts +++ b/test_files/enum/enum.ts @@ -36,6 +36,7 @@ let variableUsingExportedEnum: EnumTest2; enum ComponentIndex { Scheme = 1, UserInfo, + // TODO: b/313666408 - Fix tsc to not duplicate comments like the following Domain = 0, // Be sure to exercise the code with a 0 enum value. UserInfo2 = UserInfo, }