@zerollup/ts-transform-paths
Version:
245 lines (234 loc) • 9.51 kB
JavaScript
import ts from 'typescript';
import { ImportPathsResolver, createTraverseVisitor } from '@zerollup/ts-helpers';
import path from 'path';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var defaultConfig = {};
var fileExistsParts = ['.min.js', '.js'];
var tsParts = ['.ts', '.d.ts', '.tsx', '/index.ts', '/index.tsx', '/index.d.ts', ''];
var ImportPathInternalResolver = /** @class */ (function () {
function ImportPathInternalResolver(program, transformationContext, config) {
this.program = program;
this.config = config;
var _a = transformationContext.getCompilerOptions(), paths = _a.paths, baseUrl = _a.baseUrl;
this.resolver = new ImportPathsResolver({
paths: paths,
baseUrl: baseUrl,
exclude: config.exclude,
});
this.emitHost = transformationContext.getEmitHost
? transformationContext.getEmitHost()
: undefined;
}
ImportPathInternalResolver.prototype.fileExists = function (file) {
var _a, _b;
var _c = this, program = _c.program, emitHost = _c.emitHost;
if ((_a = program) === null || _a === void 0 ? void 0 : _a.fileExists)
return program.fileExists(file);
if ((_b = emitHost) === null || _b === void 0 ? void 0 : _b.fileExists)
return emitHost.fileExists(file);
return true;
};
ImportPathInternalResolver.prototype.resolveImport = function (oldImport, currentDir) {
var config = this.config;
var newImports = this.resolver.getImportSuggestions(oldImport, currentDir);
if (!newImports)
return;
for (var _i = 0, newImports_1 = newImports; _i < newImports_1.length; _i++) {
var newImport = newImports_1[_i];
var newImportPath = path.join(currentDir, newImport);
for (var _a = 0, tsParts_1 = tsParts; _a < tsParts_1.length; _a++) {
var part = tsParts_1[_a];
if (this.fileExists("" + newImportPath + part))
return newImport;
}
if (config.tryLoadJs) {
for (var _b = 0, fileExistsParts_1 = fileExistsParts; _b < fileExistsParts_1.length; _b++) {
var ext = fileExistsParts_1[_b];
if (this.fileExists("" + newImportPath + ext))
return "" + newImport + ext;
}
}
}
};
return ImportPathInternalResolver;
}());
function createFixNode(sf) {
var posMap = new Map();
return function fixNode(fixNode, newImport) {
/**
* This hack needed for properly d.ts paths rewrite.
* moduleSpecifier value obtained by moduleSpecifier.pos from original source file text.
* See emitExternalModuleSpecifier -> writeTextOfNode -> getTextOfNodeFromSourceText.
*
* We need to add new import path to the end of source file text and adjust moduleSpecifier.pos
*
* ts remove quoted string from output
*/
var newStr = "\"" + newImport + "\"";
var cachedPos = posMap.get(newImport);
if (cachedPos === undefined) {
cachedPos = sf.text.length;
posMap.set(newImport, cachedPos);
sf.text += newStr;
sf.end += newStr.length;
}
fixNode.pos = cachedPos;
fixNode.end = cachedPos + newStr.length;
return fixNode;
};
}
function stripQuotes(quoted) {
if (quoted[0] !== '"' && quoted[0] !== "'")
return quoted;
return quoted.substring(1, quoted.length - 1);
}
function importPathVisitor(node, _a) {
var fixNode = _a.fixNode, sf = _a.sf, resolver = _a.resolver;
var importValue;
var nodeToFix;
// dynamic import or require()
if (ts.isCallExpression(node)) {
var expression = node.expression;
if (node.arguments.length === 0)
return;
var arg = node.arguments[0];
if (!ts.isStringLiteral(arg))
return;
if (
// Can't call getText on after step
expression.getText(sf) !== 'require' &&
expression.kind !== ts.SyntaxKind.ImportKeyword)
return;
importValue = stripQuotes(arg.getText(sf));
nodeToFix = arg;
// import, export
}
else if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
if (!node.moduleSpecifier || !ts.isStringLiteral(node.moduleSpecifier))
return;
// do not use getFullText() here, bug in watch mode, https://github.com/zerkalica/zerollup/issues/12
importValue = node.moduleSpecifier.text;
nodeToFix = node.moduleSpecifier;
}
else if (ts.isImportTypeNode(node) &&
ts.isLiteralTypeNode(node.argument) &&
ts.isStringLiteral(node.argument.literal)) {
importValue = node.argument.literal.text;
}
else if (ts.isModuleDeclaration(node)) {
if (!ts.isStringLiteral(node.name))
return;
importValue = node.name.text;
nodeToFix = node.name;
}
else {
return;
}
var newImport = resolver.resolveImport(importValue, path.dirname(sf.fileName));
if (!newImport || newImport === importValue)
return;
if (nodeToFix && fixNode)
fixNode(nodeToFix, newImport);
var newSpec = ts.createLiteral(newImport);
var newNode;
if (ts.isImportTypeNode(node)) {
newNode = ts.updateImportTypeNode(node, ts.createLiteralTypeNode(newSpec), node.qualifier, node.typeArguments, node.isTypeOf);
newNode.flags = node.flags;
}
if (ts.isImportDeclaration(node)) {
newNode = ts.updateImportDeclaration(node, node.decorators, node.modifiers, node.importClause, newSpec);
/**
* Without this hack ts generates bad import of pure interface in output js,
* this causes warning "module has no exports" in bundlers.
*
* index.ts
* ```ts
* import {Some} from './lib'
* export const some: Some = { self: 'test' }
* ```
*
* lib.ts
* ```ts
* export interface Some { self: string }
* ```
*
* output: index.js
* ```js
* import { Some } from "./some/lib"
* export const some = { self: 'test' }
* ```
*/
newNode.flags = node.flags;
}
if (ts.isExportDeclaration(node)) {
var exportNode = ts.updateExportDeclaration(node, node.decorators, node.modifiers, node.exportClause, newSpec);
if (exportNode.flags !== node.flags) {
/**
* Additional hacks for exports. Without it ts throw exception, if flags changed in export node.
*/
var ms = exportNode.moduleSpecifier;
var oms = node.moduleSpecifier;
if (ms && oms) {
ms.pos = oms.pos;
ms.end = oms.end;
ms.parent = oms.parent;
}
newNode = exportNode;
newNode.flags = node.flags;
}
}
if (ts.isCallExpression(node))
newNode = ts.updateCall(node, node.expression, node.typeArguments, [
newSpec,
]);
if (ts.isModuleDeclaration(node)) {
newNode = ts.updateModuleDeclaration(node, node.decorators, node.modifiers, newSpec, node.body);
}
return newNode;
}
function transformPaths(program, configRaw) {
if (configRaw === void 0) { configRaw = defaultConfig; }
var config = __assign(__assign({}, defaultConfig), configRaw);
function createTransformer(transformationContext) {
var resolver = new ImportPathInternalResolver(program, transformationContext, config);
return function transformer(sf) {
return ts.visitNode(sf, createTraverseVisitor(importPathVisitor, {
fixNode: config.disableForDeclarations
? undefined
: createFixNode(sf),
sf: sf,
resolver: resolver,
}, transformationContext));
};
}
var plugin = {
before: createTransformer,
afterDeclarations: config.disableForDeclarations
? undefined
: createTransformer,
};
return plugin;
}
export default transformPaths;
//# sourceMappingURL=index.mjs.map