diff --git a/.gitignore b/.gitignore index e2c4bc0..c8eb475 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ -node_modules +/node_modules +/packages/*/node_modules dist lerna-debug.log junit.xml .tscc_temp yarn-error.log - diff --git a/README.md b/README.md index bebbec4..3aa72fc 100644 --- a/README.md +++ b/README.md @@ -245,7 +245,12 @@ Best practice is to provide them as a separate script tag instead of bundling it 1. Users write `import React from 'react'`, so that users' IDE can find necessary typings. 2. TSCC configures tsickle to process type declaration files of module 'react' that Typescript located for us -- usually in node_modules directory. - 3. With the help of some Typescript transformers, TSCC removes the above import statements. Doing it requires suppressing `goog.requireType('react')` and substituting `const React = require('react')` to something like `const React_1 = React`. + 3. TSCC creates a hypothetical "gluing modules" for each of external modules that looks like + ```javascript + goog.module('react') + /** Generated by TSCC */ + exports = React + ``` 4. To inform Closure about such a user-provided global variable name, TSCC generates additional externs for such a global variable, like ```javascript /** @@ -254,7 +259,7 @@ Best practice is to provide them as a separate script tag instead of bundling it */ var React = {}; ``` - tsickle writes module-scoped externs to certain mangled namespace like this, so I am grabbing that namespace to create externs like this. To my understanding, this will provide the required information to Closure, please correct me if I'm wrong. + tsickle writes module-scoped externs to certain mangled namespace like this, so by declaring a global variable to be a type of that namespace, this provides type information of the external module to Closure Compiler. #### Importing typescript sources in node_modules diff --git a/packages/tscc/src/blacklist_symbol_type.ts b/packages/tscc/src/blacklist_symbol_type.ts deleted file mode 100644 index 5d58914..0000000 --- a/packages/tscc/src/blacklist_symbol_type.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @fileoverview This module enables blacklisting types based on symbol's name. - * Tsickle's API only provide a way to blacklist certain types by their originating file's name. - * We need this to prevent tsickle to emit `goog.requireType` for external modules, where - * `TypeTranslator#isBlacklisted` is called on the string literal indicating the module's name. - * In order to achieve this, here we override the method `TypeTranslator#isBlacklisted`. Since - * this is not a public API, one should check this with every version change of tsickle. - */ -import * as ts from 'typescript'; - - -const blacklistedSymbolNames: Set = new Set(); - -export function registerTypeBlacklistedModuleName(moduleName: string) { - // Typescript double-quotes internally the string literal representing module names. - blacklistedSymbolNames.add('"' + moduleName + '"'); -} - -function symbolIsBlacklisted(symbol: ts.Symbol): boolean { - return blacklistedSymbolNames.has(symbol.name); -} - -let hasOverridden = false; - -export function patchTypeTranslator() { - if (hasOverridden) return; - hasOverridden = true; - - // Patching `isBlacklisted` exported function in "type_translator.ts" - // and a method `TypeTranslator#isBlacklisted` that references it locally. - - const typeTranslator: typeof import('tsickle/src/type_translator') - = require('tsickle/src/type_translator'); - - const __isBlacklisted = typeTranslator.isBlacklisted; - typeTranslator.isBlacklisted = function (_: Set | undefined, symbol: ts.Symbol) { - if (symbolIsBlacklisted(symbol)) return true; - return Reflect.apply(__isBlacklisted, this, arguments); - } - - const TypeTranslator = typeTranslator.TypeTranslator; - const __TypeTranslator_isBlackListed = TypeTranslator.prototype.isBlackListed; - TypeTranslator.prototype.isBlackListed = function (symbol: ts.Symbol): boolean { - if (symbolIsBlacklisted(symbol)) return true; - return Reflect.apply(__TypeTranslator_isBlackListed, this, arguments); - } -} - diff --git a/packages/tscc/src/external_module_support.ts b/packages/tscc/src/external_module_support.ts new file mode 100644 index 0000000..c6d71cc --- /dev/null +++ b/packages/tscc/src/external_module_support.ts @@ -0,0 +1,46 @@ +/** + * @fileoverview Transforms `import localName from "external_module"` to + * `const localName = global_name_for_the_external_module`. + * Also transforms `import tslib_any from 'tslib'` to `goog.require("tslib")`. + */ +import ITsccSpecWithTS from './spec/ITsccSpecWithTS'; +import {TsickleHost} from 'tsickle'; +import {moduleNameAsIdentifier} from 'tsickle/src/annotator_host'; + +export function getExternsForExternalModules(tsccSpec: ITsccSpecWithTS, tsickleHost: TsickleHost): string { + const moduleNames = tsccSpec.getExternalModuleNames(); + const toGlobalName = tsccSpec.getExternalModuleNamesToGlobalsMap(); + const header = `\n/** Generated by TSCC */` + let out = ''; + for (let moduleName of moduleNames) { + // If a module's type definition is from node_modules, its path is used as a namespace. + // otherwise, it comes from declare module '...' in user-provided files, in which the module name string + // is used as a namespace. + let typeRefFile = tsccSpec.resolveExternalModuleTypeReference(moduleName) || moduleName; + out += ` +/** + * @type{${moduleNameAsIdentifier(tsickleHost, typeRefFile)}} + * @const + */ +${tsickleHost.es5Mode ? 'var' : 'const'} ${toGlobalName[moduleName]} = {};\n`; + } + if (out.length) out = header + out; + return out; +} + +export function getGluingModules(tsccSpec: ITsccSpecWithTS, tsickleHost: TsickleHost) { + const moduleNames = tsccSpec.getExternalModuleNames(); + const toGlobalName = tsccSpec.getExternalModuleNamesToGlobalsMap(); + const out: {path: string, content: string}[] = []; + for (let moduleName of moduleNames) { + const content = `goog.module('${moduleName.replace(/([\\'])/g, '\\$1')}')\n` + + `/** Generated by TSCC */\n` + + `exports = ${toGlobalName[moduleName]};`; + // A hypothetical path of this gluing module. + let path = tsccSpec.resolveExternalModuleTypeReference(moduleName) || moduleName; + path = path.replace(/(?:\.d)?\.ts$/, '.js'); + out.push({path, content}); + } + return out; +} + diff --git a/packages/tscc/src/transformer/externalModuleTransformer.ts b/packages/tscc/src/transformer/externalModuleTransformer.ts deleted file mode 100644 index fe0eeca..0000000 --- a/packages/tscc/src/transformer/externalModuleTransformer.ts +++ /dev/null @@ -1,162 +0,0 @@ -/** - * @fileoverview Transforms `import localName from "external_module"` to - * `const localName = global_name_for_the_external_module`. - * Also transforms `import tslib_any from 'tslib'` to `goog.require("tslib")`. - */ - -import * as ts from 'typescript'; -import ITsccSpecWithTS from '../spec/ITsccSpecWithTS'; -import {TsickleHost} from 'tsickle'; -import {moduleNameAsIdentifier} from 'tsickle/src/annotator_host'; -import {createGoogCall, createSingleQuoteStringLiteral, namespaceToQualifiedName, isVariableRequireStatement} from './transformer_utils'; - - -/** - * This is a transformer run after ts transformation, before googmodule transformation. - * In order to wire imports of external modules to their global symbols, we replace - * top-level `require`s of external modules to an assignment of a local variable to - * a global symbol. This results in no `goog.require` or `goog.requireType` emit. - */ -export default function externalModuleTransformer(spec: ITsccSpecWithTS, tsickleHost: TsickleHost, typeChecker: ts.TypeChecker) - : (context: ts.TransformationContext) => ts.Transformer { - return (context: ts.TransformationContext): ts.Transformer => { - /** - * Transforms expressions accessing "default" property of a global variable of - * external modules to the global variable itself. - * Most libraries' esm version exposes its main object as its default export, - * whereas exposing itself as a global object in browser versions, and TS transforms - * references to such a local imported variable to an access to `.default` property. - * This transformation won't touch user-written `.default` access. - */ - // Copied from tsickle - const previousOnSubstituteNode = context.onSubstituteNode; - context.enableSubstitution(ts.SyntaxKind.PropertyAccessExpression); - context.onSubstituteNode = (hint, node: ts.Node): ts.Node => { - node = previousOnSubstituteNode(hint, node); - if (!ts.isPropertyAccessExpression(node)) return node; - if (node.name.text !== 'default') return node; - if (!ts.isIdentifier(node.expression)) return node; - // Find the import declaration this node comes from. - // This may be the original node, if the identifier was transformed from it. - const orig = ts.getOriginalNode(node.expression); - let importExportDecl: ts.ImportDeclaration | ts.ExportDeclaration; - if (ts.isImportDeclaration(orig) || ts.isExportDeclaration(orig)) { - importExportDecl = orig; - } else { - // Alternatively, we can try to find the declaration of the symbol. This only works for - // user-written .default accesses, the generated ones do not have a symbol associated as - // they are only produced in the CommonJS transformation, after type checking. - const sym = typeChecker.getSymbolAtLocation(node.expression); - if (!sym) return node; - const decls = sym.getDeclarations(); - if (!decls || !decls.length) return node; - const decl = decls[0]; - if (decl.parent && decl.parent.parent && ts.isImportDeclaration(decl.parent.parent)) { - importExportDecl = decl.parent.parent; - } else { - return node; - } - } - // copied from tsickle end - - // If it was an import from registered external modules, remove `.default`. - let importUrl = (importExportDecl.moduleSpecifier as ts.StringLiteral).text; - if (spec.getExternalModuleNames().includes(importUrl)) { - return node.expression; - } - return node; - }; - - return (sf: ts.SourceFile): ts.SourceFile => { - function maybeExternalModuleRequire( - original: ts.Statement, importedUrl: string, newIdent: ts.Identifier - ) { - const setOriginalNode = (range: ts.Statement) => { - return ts.setOriginalNode(ts.setTextRange(range, original), original); - } - /** - * require('tslib') requires special treatment - map it to goog.require('tslib') - * It needs to be linked to a custom `tslib` provided with tsickle (which seems not - * to be included in tsickle npm package) which declares module name `tslib`. - * Due to the `pathToModuleName` actually resolving module names to generate goog - * module names, if unchanged here `require('tslib')` will end up being something like - * `goog.require('node_modules.tslib.tslib')`. - */ - if (importedUrl === 'tslib') { - let varDecl = ts.createVariableDeclaration( - newIdent, undefined, createGoogCall('require', createSingleQuoteStringLiteral('tslib')) - ); - return setOriginalNode(ts.createVariableStatement( - undefined, ts.createVariableDeclarationList( - [varDecl], tsickleHost.es5Mode ? undefined : ts.NodeFlags.Const - ) - )) - } - const globalName = spec.getExternalModuleNamesToGlobalsMap()[importedUrl]; - if (typeof globalName === 'undefined') return null; - // We must figure out on what namespace the extern for this module is defined. - // If it was from `declare module "..."` in a user-provided file, it comes from 'getIdentifierText' - // If it was from exported declaration file found in node_modules, - // it is converted from some$path$like$this which is derived from moduleNameAsIdentifier function. - // This relies on the heuristic of tsickle, so must be carefully validated whenever tsickle updates. - if (newIdent.escapedText === globalName) { - // Name of the introduced identifier coincides with the global identifier, - // no need to emit things. - return setOriginalNode(ts.createEmptyStatement()); - } - // Convert `const importedName = require('externalModuleName')` to: - // `const importedName = GlobalName;` - return setOriginalNode(ts.createVariableStatement( - undefined, - ts.createVariableDeclarationList( - [ - ts.createVariableDeclaration( - newIdent, - undefined, - namespaceToQualifiedName(globalName) - ) - ], - tsickleHost.es5Mode ? undefined : ts.NodeFlags.Const) - )); - } - - function visitTopLevelStatement(statements: ts.Statement[], sf: ts.SourceFile, node: ts.Statement) { - lookupExternalModuleRequire: { - let _ = isVariableRequireStatement(node); - if (!_) break lookupExternalModuleRequire; - let {importedUrl, newIdent} = _; - const require = maybeExternalModuleRequire(node, importedUrl, newIdent); - if (!require) break lookupExternalModuleRequire; - statements.push(require); - return; - } - statements.push(node); - } - - const stmts: ts.Statement[] = []; - for (const stmt of sf.statements) visitTopLevelStatement(stmts, sf, stmt); - return ts.updateSourceFileNode(sf, ts.setTextRange(ts.createNodeArray(stmts), sf.statements)); - } - } -} - -export function getExternsForExternalModules(tsccSpec: ITsccSpecWithTS, tsickleHost: TsickleHost): string { - const moduleNames = tsccSpec.getExternalModuleNames(); - const toGlobalName = tsccSpec.getExternalModuleNamesToGlobalsMap(); - const header = `\n/** Generated by TSCC */` - let out = ''; - for (let moduleName of moduleNames) { - // If a module's type definition is from node_modules, its path is used as a namespace. - // otherwise, it comes from declare module '...' in user-provided files, in which the module name string - // is used as a namespace. - let typeRefFile = tsccSpec.resolveExternalModuleTypeReference(moduleName) || moduleName; - out += ` -/** - * @type{${moduleNameAsIdentifier(tsickleHost, typeRefFile)}} - * @const - */ -${tsickleHost.es5Mode ? 'var' : 'const'} ${toGlobalName[moduleName]} = {};\n`; - } - if (out.length) out = header + out; - return out; -} diff --git a/packages/tscc/src/tscc.ts b/packages/tscc/src/tscc.ts index a879412..f57f7b5 100644 --- a/packages/tscc/src/tscc.ts +++ b/packages/tscc/src/tscc.ts @@ -3,7 +3,6 @@ import chalk from 'chalk'; import * as StreamArray from 'stream-json/streamers/StreamArray'; import * as tsickle from "tsickle"; import * as ts from "typescript"; -import {patchTypeTranslator, registerTypeBlacklistedModuleName} from './blacklist_symbol_type'; import getDefaultLibs from './default_libs'; import {Cache, FSCacheAccessor} from './graph/Cache'; import ClosureDependencyGraph from './graph/ClosureDependencyGraph'; @@ -18,7 +17,7 @@ import spawnCompiler from './spawn_compiler'; import ITsccSpecWithTS from "./spec/ITsccSpecWithTS"; import TsccSpecWithTS, {TsError} from "./spec/TsccSpecWithTS"; import decoratorPropertyTransformer from './transformer/decoratorPropertyTransformer'; -import externalModuleTransformer, {getExternsForExternalModules} from './transformer/externalModuleTransformer'; +import {getExternsForExternalModules, getGluingModules} from './external_module_support'; import fs = require('fs'); import path = require('path'); import stream = require('stream'); @@ -42,19 +41,19 @@ export default async function tscc( tsccSpecJSONOrItsPath: string | IInputTsccSpecJSON, tsConfigPathOrTsArgs?: string, compilerOptionsOverride?: object -):Promise +): Promise /** @internal */ export default async function tscc( tsccSpecJSONOrItsPath: string | IInputTsccSpecJSON, tsConfigPathOrTsArgs: string[], compilerOptionsOverride?: object -):Promise +): Promise /** @internal */ export default async function tscc( tsccSpecJSONOrItsPath: string | IInputTsccSpecJSON, tsConfigPathOrTsArgs?: string | string[], compilerOptionsOverride?: object -):Promise { +): Promise { const tsccLogger = new Logger(chalk.green("TSCC: "), process.stderr); const tsLogger = new Logger(chalk.blue("TS: "), process.stderr); @@ -115,11 +114,16 @@ export default async function tscc( writeFile(path, fs.readFileSync(path, 'utf8')) }) + // Manually push gluing modules + getGluingModules(tsccSpec, transformerHost).forEach(({path, content}) => { + writeFile(path, content) + }) + patchTsickleResolveModule(); // check comments in its source - required to generate proper externs const result = tsickle.emit(program, transformerHost, writeFile, undefined, undefined, false, { afterTs: [ decoratorPropertyTransformer(transformerHost), - externalModuleTransformer(tsccSpec, transformerHost, program.getTypeChecker()) + //externalModuleTransformer(tsccSpec, transformerHost, program.getTypeChecker()) ] }); // If tsickle errors, print diagnostics and exit. @@ -303,26 +307,17 @@ function getTsickleHost(tsccSpec: ITsccSpecWithTS, logger: Logger): tsickle.Tsic const externalModuleNames = tsccSpec.getExternalModuleNames(); const resolvedExternalModuleTypeRefs: string[] = []; - let hasTypeBlacklistedSymbols = false; - for (let i = 0, l = externalModuleNames.length; i < l; i++) { - let name = externalModuleNames[i]; + for (let name of externalModuleNames) { let typeRefFileName = tsccSpec.resolveExternalModuleTypeReference(name); if (typeRefFileName) { resolvedExternalModuleTypeRefs.push(typeRefFileName); - } else { - // Can't achieve blacklisting via TsickleHost.typeBlacklistPaths API - hasTypeBlacklistedSymbols = true; - registerTypeBlacklistedModuleName(name); } } - if (hasTypeBlacklistedSymbols) { - patchTypeTranslator(); - } const externalModuleRoots = resolvedExternalModuleTypeRefs .map(getPackageBoundary); - const ignoreWarningsPath = tsccSpec.debug().ignoreWarningsPath || ["/node_modules"]; + const ignoreWarningsPath = tsccSpec.debug().ignoreWarningsPath || ["/node_modules/"]; const transformerHost: tsickle.TsickleHost = { shouldSkipTsickleProcessing(fileName: string) { @@ -344,7 +339,7 @@ function getTsickleHost(tsccSpec: ITsccSpecWithTS, logger: Logger): tsickle.Tsic googmodule: true, transformDecorators: true, transformTypesToClosure: true, - typeBlackListPaths: new Set(resolvedExternalModuleTypeRefs), + typeBlackListPaths: new Set(), untyped: false, logWarning(warning) { if (warning.file) { @@ -369,6 +364,11 @@ function getTsickleHost(tsccSpec: ITsccSpecWithTS, logger: Logger): tsickle.Tsic * deterministic output based on a single file. */ pathToModuleName: (context: string, fileName: string) => { + // Module names specified as external are not resolved, which in effect cause + // googmodule transformer to emit module names verbatim in `goog.require()`. + if (externalModuleNames.includes(fileName)) return fileName; + // 'tslib' is always considered as an external module. + if (fileName === 'tslib') return fileName; // Resolve module via ts API const resolved = ts.resolveModuleName(fileName, context, options, compilerHost); if (resolved && resolved.resolvedModule) { diff --git a/packages/tscc/test/e2e/__snapshots__/golden_test.ts.snap b/packages/tscc/test/e2e/__snapshots__/golden_test.ts.snap index 1e067b0..48b11f7 100644 --- a/packages/tscc/test/e2e/__snapshots__/golden_test.ts.snap +++ b/packages/tscc/test/e2e/__snapshots__/golden_test.ts.snap @@ -66,3 +66,14 @@ function d(){}d.prototype.a=function(){console.log(\\"a\\")};(function(e,b,c,f){ `; exports[`tscc e2e case_3_sourcemaps_with_decorators: case_3_sourcemaps_with_decorators/a.js.map 1`] = `"{\\"version\\":3,\\"sources\\":[\\"a.ts\\",\\"../../../../third_party/tsickle/third_party/tslib/tslib.js\\"],\\"names\\":[\\"A\\",\\"b\\",\\"console\\",\\"log\\",\\"exports.__decorate\\",\\"decorators\\",\\"target\\",\\"key\\",\\"desc\\",\\"c\\",\\"arguments\\",\\"length\\",\\"r\\",\\"Object\\",\\"getOwnPropertyDescriptor\\",\\"d\\",\\"Reflect\\",\\"i\\",\\"defineProperty\\",\\"decorator\\",\\"prop\\",\\"prototype\\"],\\"mappings\\":\\"A;;;;;;;;;;;;;;AAIA,QAAMA,EAAN,EAAA,EAEC,CAAA,UAAA,EAAAC,CAAAA,QAAC,EAAA,CAAIC,OAAAC,IAAA,CAAY,GAAZ,CAAJ,CCmEmBC,UAAS,CAACC,CAAD,CAAaC,CAAb,CAAqBC,CAArB,CAA0BC,CAA1B,CAAgC,CAAA,IACtDC,EAAIC,SAAAC,OADkD,CAChCC,EAAQ,CAAJ,CAAAH,CAAA,CAAQH,CAAR,CAA0B,IAAT,GAAAE,CAAA,CAAgBA,CAAhB,CAAuBK,MAAAC,yBAAA,CAAgCR,CAAhC,CAAwCC,CAAxC,CAAvB,CAAsEC,CAD3D,CACiEO,CAG3H,IAAuB,QAAvB,GAAI,MAAOC,QAAX,EAAmCA,OAAnC,EAA6E,UAA7E,GAA8C,MAAOA,QAAA,SAArD,CAAyFJ,CAAA,CAAII,OAAA,SAAA,CAAoBX,CAApB,CAAgCC,CAAhC,CAAwCC,CAAxC,CAA6CC,CAA7C,CAA7F,KACK,KAAK,IAAIS,EAAIZ,CAAAM,OAAJM,CAAwB,CAAjC,CAAyC,CAAzC,EAAoCA,CAApC,CAA4CA,CAAA,EAA5C,CAAiD,GAAIF,CAAJ,CAAQV,CAAA,CAAWY,CAAX,CAAR,CAAuBL,CAAA,EAAS,CAAJ,CAAAH,CAAA,CAAQM,CAAA,CAAEH,CAAF,CAAR,CAAmB,CAAJ,CAAAH,CAAA,CAAQM,CAAA,CAAET,CAAF,CAAUC,CAAV,CAAeK,CAAf,CAAR,CAA4BG,CAAA,CAAET,CAAF,CAAUC,CAAV,CAAhD,GAAmEK,CAChJ,OAAW,EAAJ,CAAAH,CAAA,EAASG,CAAT,EAAcC,MAAAK,eAAA,CAAsBZ,CAAtB,CAA8BC,CAA9B,CAAmCK,CAAnC,CAAd,CAAqDA,CANF,CAAzCR,CDlEpB,CAAA,CAPDe,QAAkB,CAACb,CAAD,CAASc,CAAT,CAA2BZ,CAA3B,CAAkD,CACnE,MAAOA,EAD4D,CAOnE,CAAA,CAFUR,CAAAqB,UAEV,CAFU,GAEV,CAFU,IAEV,CAEDpB,EAAC,IAAID,CAALC,GAAA\\",\\"file\\":\\".tscc_temp/case_3_sourcemaps_with_decorators/a.js\\"}"`; + +exports[`tscc e2e case_4_external: case_4_external 1`] = ` +Array [ + "a.js", +] +`; + +exports[`tscc e2e case_4_external: case_4_external/a.js 1`] = ` +"var a=CollidesWithGlobalName;anExternalModule.a();window.this_should_survive=!0;a.b(); +" +`; diff --git a/packages/tscc/test/e2e/sample/case_4_external/a.ts b/packages/tscc/test/e2e/sample/case_4_external/a.ts new file mode 100644 index 0000000..0eb1c37 --- /dev/null +++ b/packages/tscc/test/e2e/sample/case_4_external/a.ts @@ -0,0 +1,15 @@ +import * as thing from 'an-external-module' +import * as CollidesWithGlobalName from 'another-external-module'; + +thing.a(); + +(function() { + // Shadowed variable, just in case + var thing = { + a: function() { window["this_should_survive"] = true; } + }; + thing.a(); +})(); + +CollidesWithGlobalName.b(); + diff --git a/packages/tscc/test/e2e/sample/case_4_external/node_modules/an-external-module/index.d.ts b/packages/tscc/test/e2e/sample/case_4_external/node_modules/an-external-module/index.d.ts new file mode 100644 index 0000000..d518a8a --- /dev/null +++ b/packages/tscc/test/e2e/sample/case_4_external/node_modules/an-external-module/index.d.ts @@ -0,0 +1,5 @@ +declare namespace anExternalModule { + export function a():void +} + +export = anExternalModule; diff --git a/packages/tscc/test/e2e/sample/case_4_external/node_modules/an-external-module/package.json b/packages/tscc/test/e2e/sample/case_4_external/node_modules/an-external-module/package.json new file mode 100644 index 0000000..7721d23 --- /dev/null +++ b/packages/tscc/test/e2e/sample/case_4_external/node_modules/an-external-module/package.json @@ -0,0 +1,3 @@ +{ + "main": "index.js" +} diff --git a/packages/tscc/test/e2e/sample/case_4_external/node_modules/another-external-module/index.d.ts b/packages/tscc/test/e2e/sample/case_4_external/node_modules/another-external-module/index.d.ts new file mode 100644 index 0000000..51edc07 --- /dev/null +++ b/packages/tscc/test/e2e/sample/case_4_external/node_modules/another-external-module/index.d.ts @@ -0,0 +1,5 @@ +declare namespace anotherExternalModule { + export function b():void +} + +export = anotherExternalModule; diff --git a/packages/tscc/test/e2e/sample/case_4_external/node_modules/another-external-module/package.json b/packages/tscc/test/e2e/sample/case_4_external/node_modules/another-external-module/package.json new file mode 100644 index 0000000..7721d23 --- /dev/null +++ b/packages/tscc/test/e2e/sample/case_4_external/node_modules/another-external-module/package.json @@ -0,0 +1,3 @@ +{ + "main": "index.js" +} diff --git a/packages/tscc/test/e2e/sample/case_4_external/tscc.spec.json b/packages/tscc/test/e2e/sample/case_4_external/tscc.spec.json new file mode 100644 index 0000000..640a4c8 --- /dev/null +++ b/packages/tscc/test/e2e/sample/case_4_external/tscc.spec.json @@ -0,0 +1,10 @@ +{ + "modules": { + "a": "./a.ts" + }, + "external": { + "an-external-module": "anExternalModule", + "another-external-module": "CollidesWithGlobalName" + }, + "prefix": ".tscc_temp/case_4_external/" +} diff --git a/packages/tscc/test/e2e/sample/case_4_external/tsconfig.json b/packages/tscc/test/e2e/sample/case_4_external/tsconfig.json new file mode 100644 index 0000000..231027e --- /dev/null +++ b/packages/tscc/test/e2e/sample/case_4_external/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es5" + }, + "include": [ + "./*.ts" + ] +}