@ts-liveserver/ts-transpiler
Advanced tools
Comparing version 0.0.1 to 0.0.2
module.exports = require("../../.eslintrc.js"); | ||
module.exports.rule["prefer-const"] = "off"; | ||
module.exports.rules["prefer-const"] = "off"; | ||
module.exports.rules["no-console"] = "off"; |
@@ -6,2 +6,2 @@ module.exports = { | ||
singleQuote: false, | ||
} | ||
}; |
import ModuleB from "./ModuleB.ts"; | ||
const component = React.createElement("div", null, "Hello"); | ||
component; | ||
console.log(component); | ||
const ModuleA = ModuleB; | ||
export default ModuleA; | ||
//# sourceMappingURL=ModuleA.js.map |
@@ -5,27 +5,92 @@ import CommonJsTransformer from '../../src/transformers/CommonJsTransformer' | ||
describe('CommonJsTransformer', () => { | ||
it('Should convert to default export to ES6', async () => { | ||
const input = 'module.exports = {};' | ||
const output = 'export default {};' | ||
expect(await transform(input)).toBe(output) | ||
describe('Import', () => { | ||
it('Should convert simple import', async () => { | ||
const input = 'require("./hello.ts")' | ||
const output = 'import "./hello.ts";' | ||
expect(await transformWithPlugin(input)).toBe(output) | ||
}) | ||
it('Should convert default import to ES6', async () => { | ||
const input = 'const Hello = require("./hello.ts")' | ||
const output = 'import * as Hello from "./hello.ts";' | ||
expect(await transformWithPlugin(input)).toBe(output) | ||
}) | ||
it('Should convert named import', async () => { | ||
const input = 'const { Hello } = require("./hello.ts")' | ||
const output = 'import { Hello } from "./hello.ts";' | ||
expect(await transformWithPlugin(input)).toBe(output) | ||
}) | ||
it('Should convert require in sub-scope', async () => { | ||
const input = '{ const hello = require("hello.js") }' | ||
const output = | ||
'import * as GENERATED_VAR_BY_TRANSFORMER_1 from "hello.js"; { const hello = GENERATED_VAR_BY_TRANSFORMER_1; }' | ||
expect(await transformWithPlugin(input)).toBe( | ||
await transformWithoutPlugin(output), | ||
) | ||
}) | ||
}) | ||
it('Should convert to default export to ES6', async () => { | ||
const input = 'module.exports = {};' | ||
const output = 'export default {};' | ||
expect(await transform(input)).toBe(output) | ||
describe('Forwards', () => { | ||
it('Should convert redirects', async () => { | ||
const input = 'module.exports = require("./hello.js")' | ||
const output = 'export * from "./hello.js";' | ||
expect(await transformWithPlugin(input)).toBe(output) | ||
}) | ||
}) | ||
it('Should convert default import to ES6', async () => { | ||
const input = 'const Hello = require("./hello.ts");' | ||
const output = 'import Hello from "./hello.ts";' | ||
expect(await transform(input)).toBe(output) | ||
describe('Exports', () => { | ||
it('Should convert to default export to ES6', async () => { | ||
const input = 'module.exports = Hello' | ||
const output = 'export default Hello' | ||
expect(await transformWithPlugin(input)).toBe( | ||
await transformWithoutPlugin(output), | ||
) | ||
}) | ||
it('Should convert exports.name', async () => { | ||
const input = 'exports.hello = Hello' | ||
const output = 'export { Hello as hello }' | ||
expect(await transformWithPlugin(input)).toBe( | ||
await transformWithoutPlugin(output), | ||
) | ||
}) | ||
it('Should convert exports.name with same name as the parameter', async () => { | ||
const input = 'const hello = null; exports.hello = hello' | ||
const output = 'const hello = null; export { hello }' | ||
expect(await transformWithPlugin(input)).toBe( | ||
await transformWithoutPlugin(output), | ||
) | ||
}) | ||
it('Should convert exports.default', async () => { | ||
const input = 'exports.default = Hello' | ||
const output = 'export default Hello' | ||
expect(await transformWithPlugin(input)).toBe( | ||
await transformWithoutPlugin(output), | ||
) | ||
}) | ||
it('Should convert define.property with getter', async () => { | ||
const input = | ||
'Object.defineProperty(exports, "hello", { enumerable: true, get: function () { return parts_js_1.PropertyPart; } });' | ||
const output = | ||
'var GENERATED_VAR_BY_TRANSFORMER_1 = parts_js_1.PropertyPart; export { GENERATED_VAR_BY_TRANSFORMER_1 as hello };' | ||
expect(await transformWithPlugin(input)).toBe( | ||
await transformWithoutPlugin(output), | ||
) | ||
}) | ||
it('Should convert define.property for identifier', async () => { | ||
const input = 'Object.defineProperty(exports, "a", b });' | ||
const output = 'export { b as a };' | ||
expect(await transformWithPlugin(input)).toBe( | ||
await transformWithoutPlugin(output), | ||
) | ||
}) | ||
it('Should convert define.property', async () => { | ||
const input = 'module.exports = { a: b, c: d }' | ||
const output = 'export { b as a, d as c };' | ||
expect(await transformWithPlugin(input)).toBe( | ||
await transformWithoutPlugin(output), | ||
) | ||
}) | ||
it('Should convert to default export', async () => { | ||
const input = 'module.exports.hello = Hello;' | ||
const output = 'export { Hello as hello };' | ||
expect(await transformWithPlugin(input)).toBe(output) | ||
}) | ||
}) | ||
it('Should convert simple import', async () => { | ||
const input = 'require("./hello.ts");' | ||
const output = 'import "./hello.ts";' | ||
expect(await transform(input)).toBe(output) | ||
}) | ||
it('Should convert named import', async () => { | ||
const input = 'const { Hello } = require("./hello.ts");' | ||
const output = 'import { Hello } from "./hello.ts";' | ||
expect(await transform(input)).toBe(output) | ||
}) | ||
}) | ||
@@ -37,3 +102,3 @@ | ||
checkJs: false, | ||
noResolve: false, | ||
noResolve: true, | ||
esModuleInterop: true, | ||
@@ -51,6 +116,6 @@ skipLibCheck: true, | ||
const transformers: TypeScript.CustomTransformers = { | ||
after: [(context) => new CommonJsTransformer(context)], | ||
before: [(context) => new CommonJsTransformer(context)], | ||
} | ||
async function transform(code: string): Promise<string> { | ||
async function transformWithPlugin(code: string): Promise<string> { | ||
const results = await TypeScript.transpileModule(code, { | ||
@@ -63,1 +128,9 @@ compilerOptions: compilerOptions, | ||
} | ||
async function transformWithoutPlugin(code: string): Promise<string> { | ||
const results = await TypeScript.transpileModule(code, { | ||
compilerOptions: compilerOptions, | ||
fileName: 'hello.ts', | ||
}) | ||
return results.outputText.trim() | ||
} |
@@ -9,5 +9,7 @@ import TsTranspiler from '../src/TsTranspiler' | ||
const transpiler = new TsTranspiler() | ||
const results = await transpiler.transformFile( | ||
Path.resolve(__dirname, 'inputData', inputFileName), | ||
) | ||
const results = ( | ||
await transpiler.transformFile( | ||
Path.resolve(__dirname, 'inputData', inputFileName), | ||
) | ||
).outputText | ||
expect(results + '\n').toBe( | ||
@@ -14,0 +16,0 @@ Fs.readFileSync( |
@@ -1,1 +0,1 @@ | ||
module.exports = require("../../.prettierrc.js"); | ||
module.exports = require('../../.prettierrc.js') |
import TypeScript from 'typescript'; | ||
export default class CommonJsTransformer implements TypeScript.CustomTransformer { | ||
private context; | ||
private counter; | ||
constructor(context: TypeScript.TransformationContext); | ||
private visit; | ||
transformSourceFile(node: TypeScript.SourceFile): TypeScript.SourceFile; | ||
transformBundle(node: TypeScript.Bundle): TypeScript.Bundle; | ||
transformBundle(): TypeScript.Bundle; | ||
transformSourceFile(sourceFile: TypeScript.SourceFile): TypeScript.SourceFile; | ||
private generateUniqueName; | ||
private convertDefinePropery; | ||
private convertToEsmImport; | ||
private convertToEsmExport; | ||
private stripModule; | ||
private requireTopScope; | ||
} |
@@ -7,2 +7,3 @@ "use strict"; | ||
const typescript_1 = __importDefault(require("typescript")); | ||
const KEYNAME_EXPORTS = 'exports'; | ||
/* | ||
@@ -13,15 +14,230 @@ Transpile CommonJS to ES6 module | ||
constructor(context) { | ||
this.counter = 0; | ||
this.context = context; | ||
} | ||
visit(node) { | ||
return typescript_1.default.visitEachChild(node, this.visit.bind(this), this.context); | ||
transformBundle() { | ||
throw new Error('Method not implemented.'); | ||
} | ||
transformSourceFile(node) { | ||
return typescript_1.default.visitNode(node, this.visit.bind(this)); | ||
transformSourceFile(sourceFile) { | ||
const requireInTopScope = this.requireTopScope(sourceFile); | ||
const withoutModule = this.stripModule(requireInTopScope); | ||
const withoutDefineProperty = this.convertDefinePropery(withoutModule); | ||
const esmExport = this.convertToEsmExport(withoutDefineProperty); | ||
return this.convertToEsmImport(esmExport); | ||
} | ||
transformBundle(node) { | ||
return node; | ||
// Generate a file unique variable name | ||
generateUniqueName() { | ||
this.counter++; | ||
return 'GENERATED_VAR_BY_TRANSFORMER_' + String(this.counter); | ||
} | ||
// Convert all Object.defineProperty(exports, "hello", Hello);" | ||
convertDefinePropery(sourceFile) { | ||
const visit = (node) => { | ||
if (typescript_1.default.isCallExpression(node) && | ||
typescript_1.default.isPropertyAccessExpression(node.expression) && | ||
typescript_1.default.isIdentifier(node.expression.expression) && | ||
typescript_1.default.isIdentifier(node.expression.name) && | ||
node.expression.expression.text === 'Object' && | ||
node.expression.name.text === 'defineProperty' && | ||
typescript_1.default.isIdentifier(node.arguments[0])) { | ||
const firstArgument = node.arguments[0]; | ||
const secondArgument = node.arguments[1]; | ||
const thirdArgument = node.arguments[2]; | ||
if (typescript_1.default.isIdentifier(firstArgument) && | ||
firstArgument.text === KEYNAME_EXPORTS && | ||
typescript_1.default.isStringLiteral(secondArgument)) { | ||
if (typescript_1.default.isIdentifier(thirdArgument)) { | ||
return typescript_1.default.factory.createBinaryExpression(typescript_1.default.factory.createPropertyAccessExpression(typescript_1.default.factory.createIdentifier(KEYNAME_EXPORTS), typescript_1.default.factory.createIdentifier(secondArgument.text)), typescript_1.default.SyntaxKind.EqualsToken, thirdArgument); | ||
} | ||
else if (typescript_1.default.isObjectLiteralExpression(thirdArgument)) { | ||
const propertyGetter = thirdArgument.properties | ||
.filter((property) => typescript_1.default.isPropertyAssignment(property)) | ||
.find((property) => typescript_1.default.isPropertyAssignment(property) && | ||
typescript_1.default.isIdentifier(property.name) && | ||
property.name.text === 'get'); | ||
if (propertyGetter && | ||
typescript_1.default.isPropertyAssignment(propertyGetter) && | ||
propertyGetter.initializer && | ||
typescript_1.default.isFunctionExpression(propertyGetter.initializer) && | ||
typescript_1.default.isBlock(propertyGetter.initializer.body)) { | ||
const firstStatement = propertyGetter.initializer.body.statements[0]; | ||
if (typescript_1.default.isReturnStatement(firstStatement) && | ||
firstStatement.expression) { | ||
return typescript_1.default.factory.createBinaryExpression(typescript_1.default.factory.createPropertyAccessExpression(typescript_1.default.factory.createIdentifier(KEYNAME_EXPORTS), typescript_1.default.factory.createIdentifier(secondArgument.text)), typescript_1.default.SyntaxKind.EqualsToken, firstStatement.expression); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return typescript_1.default.visitEachChild(node, visit, this.context); | ||
}; | ||
return typescript_1.default.visitNode(sourceFile, visit); | ||
} | ||
// Top level CommonJS to ESM | ||
convertToEsmImport(sourceFile) { | ||
const visit = (node) => { | ||
// Import without name | ||
if (typescript_1.default.isExpressionStatement(node) && | ||
typescript_1.default.isCallExpression(node.expression) && | ||
typescript_1.default.isIdentifier(node.expression.expression) && | ||
node.expression.expression.text === 'require' && | ||
node.expression.arguments.length === 1) { | ||
const argument = node.expression.arguments[0]; | ||
if (typescript_1.default.isStringLiteralLike(argument)) { | ||
return typescript_1.default.factory.createImportDeclaration(undefined, undefined, undefined, typescript_1.default.factory.createStringLiteral(argument.text)); | ||
} | ||
} | ||
// Import with reference | ||
if (typescript_1.default.isVariableStatement(node) && | ||
typescript_1.default.isVariableDeclarationList(node.declarationList)) { | ||
const importDeclarations = []; | ||
const variableDeclarations = []; | ||
for (const variableDeclaration of node.declarationList.declarations) { | ||
if (variableDeclaration.initializer && | ||
typescript_1.default.isCallExpression(variableDeclaration.initializer) && | ||
typescript_1.default.isIdentifier(variableDeclaration.initializer.expression) && | ||
variableDeclaration.initializer.expression.text === 'require' && | ||
variableDeclaration.initializer.arguments.length === 1) { | ||
const argument = variableDeclaration.initializer.arguments[0]; | ||
if (typescript_1.default.isStringLiteral(argument) && | ||
typescript_1.default.isIdentifier(variableDeclaration.name)) { | ||
importDeclarations.push(typescript_1.default.factory.createImportDeclaration(undefined, undefined, typescript_1.default.factory.createImportClause(false, undefined, typescript_1.default.factory.createNamespaceImport(variableDeclaration.name)), argument)); | ||
} | ||
else if (typescript_1.default.isStringLiteral(argument) && | ||
typescript_1.default.isObjectBindingPattern(variableDeclaration.name)) { | ||
importDeclarations.push(typescript_1.default.factory.createImportDeclaration(undefined, undefined, typescript_1.default.factory.createImportClause(false, undefined, typescript_1.default.factory.createNamedImports(variableDeclaration.name.elements.map((bindingElement) => typescript_1.default.factory.createImportSpecifier(bindingElement.propertyName, bindingElement.name)))), typescript_1.default.factory.createStringLiteral(argument.text))); | ||
} | ||
} | ||
else { | ||
variableDeclarations.push(variableDeclaration); | ||
} | ||
} | ||
if (variableDeclarations.length === 0) { | ||
return importDeclarations; | ||
} | ||
else { | ||
return [ | ||
...importDeclarations, | ||
typescript_1.default.factory.updateVariableStatement(node, undefined, typescript_1.default.factory.updateVariableDeclarationList(node.declarationList, variableDeclarations)), | ||
]; | ||
} | ||
} | ||
return node; | ||
}; | ||
return typescript_1.default.visitEachChild(sourceFile, visit, this.context); | ||
} | ||
// Top level CommonJS to ESM | ||
convertToEsmExport(sourceFile) { | ||
const visit = (node) => { | ||
if (typescript_1.default.isExpressionStatement(node) && | ||
typescript_1.default.isBinaryExpression(node.expression) && | ||
node.expression.operatorToken.kind === typescript_1.default.SyntaxKind.EqualsToken) { | ||
// exports.something = something; | ||
if (typescript_1.default.isPropertyAccessExpression(node.expression.left) && | ||
typescript_1.default.isIdentifier(node.expression.left.name) && | ||
typescript_1.default.isIdentifier(node.expression.left.expression) && | ||
node.expression.left.expression.text === KEYNAME_EXPORTS) { | ||
// exports.default = something; | ||
if (node.expression.left.name.text === 'default') { | ||
return typescript_1.default.factory.createExportAssignment(undefined, undefined, undefined, node.expression.right); | ||
} | ||
// exports.something = anIdentifier; | ||
else if (typescript_1.default.isIdentifier(node.expression.right)) { | ||
return typescript_1.default.factory.createExportDeclaration(undefined, undefined, false, typescript_1.default.factory.createNamedExports([ | ||
typescript_1.default.factory.createExportSpecifier(node.expression.right.text === node.expression.left.name.text | ||
? undefined | ||
: node.expression.right, node.expression.left.name), | ||
])); | ||
} | ||
else { | ||
const newIdentifierName = this.generateUniqueName(); | ||
const variableStatement = typescript_1.default.factory.createVariableStatement(undefined, [ | ||
typescript_1.default.factory.createVariableDeclaration(newIdentifierName, undefined, undefined, node.expression.right), | ||
]); | ||
return [ | ||
variableStatement, | ||
typescript_1.default.factory.createExportDeclaration(undefined, undefined, false, typescript_1.default.factory.createNamedExports([ | ||
typescript_1.default.factory.createExportSpecifier(typescript_1.default.factory.createIdentifier(newIdentifierName), node.expression.left.name), | ||
])), | ||
]; | ||
} | ||
} | ||
// exports = something; | ||
if (typescript_1.default.isIdentifier(node.expression.left) && | ||
node.expression.left.text === KEYNAME_EXPORTS) { | ||
// exports = { a: false, b: true } | ||
if (typescript_1.default.isObjectLiteralExpression(node.expression.right)) { | ||
const exportSpecifiers = []; | ||
for (const property of node.expression.right.properties) { | ||
if (typescript_1.default.isPropertyAssignment(property) && | ||
typescript_1.default.isIdentifier(property.name) && | ||
typescript_1.default.isIdentifier(property.initializer)) { | ||
exportSpecifiers.push(typescript_1.default.factory.createExportSpecifier(property.initializer.text === property.name.text | ||
? undefined | ||
: property.initializer, property.name)); | ||
} | ||
} | ||
return typescript_1.default.factory.createExportDeclaration(undefined, undefined, false, typescript_1.default.factory.createNamedExports(exportSpecifiers)); | ||
} | ||
// exports = require('hello.js'); | ||
else if (typescript_1.default.isCallExpression(node.expression.right) && | ||
typescript_1.default.isIdentifier(node.expression.right.expression) && | ||
node.expression.right.expression.text === 'require' && | ||
typescript_1.default.isStringLiteral(node.expression.right.arguments[0])) { | ||
return typescript_1.default.factory.createExportDeclaration(undefined, undefined, false, undefined, node.expression.right.arguments[0]); | ||
} | ||
// exports = 'hello' or exports = Hello | ||
else { | ||
return typescript_1.default.factory.createExportAssignment(undefined, undefined, undefined, node.expression.right); | ||
} | ||
} | ||
} | ||
return node; | ||
}; | ||
return typescript_1.default.visitEachChild(sourceFile, visit, this.context); | ||
} | ||
// module.exports -> exports | ||
stripModule(sourceFile) { | ||
const visit = (node) => { | ||
if (typescript_1.default.isPropertyAccessExpression(node) && | ||
typescript_1.default.isIdentifier(node.expression) && | ||
typescript_1.default.isIdentifier(node.name) && | ||
node.expression.text === 'module' && | ||
node.name.text === KEYNAME_EXPORTS) { | ||
return node.name; | ||
} | ||
return typescript_1.default.visitEachChild(node, visit, this.context); | ||
}; | ||
return typescript_1.default.visitNode(sourceFile, visit); | ||
} | ||
// Move all require-calls to top-scope | ||
requireTopScope(sourceFile) { | ||
const newStatements = []; | ||
const visit = (node) => { | ||
const inRootScope = node?.parent?.parent?.parent?.parent && | ||
typescript_1.default.isVariableDeclaration(node.parent) && | ||
typescript_1.default.isVariableDeclarationList(node.parent.parent) && | ||
typescript_1.default.isVariableStatement(node.parent.parent.parent) && | ||
typescript_1.default.isSourceFile(node.parent.parent.parent.parent); | ||
if (typescript_1.default.isCallExpression(node) && | ||
typescript_1.default.isIdentifier(node.expression) && | ||
node.arguments.length === 1 && | ||
node.expression.text === 'require' && | ||
inRootScope === false) { | ||
const newIdentifierName = this.generateUniqueName(); | ||
newStatements.push(typescript_1.default.factory.createVariableStatement(undefined, [ | ||
typescript_1.default.factory.createVariableDeclaration(newIdentifierName, undefined, undefined, node), | ||
])); | ||
return typescript_1.default.factory.createIdentifier(newIdentifierName); | ||
} | ||
return typescript_1.default.visitEachChild(node, visit, this.context); | ||
}; | ||
const changedSourceFile = typescript_1.default.visitNode(sourceFile, visit); | ||
return typescript_1.default.factory.updateSourceFile(changedSourceFile, [ | ||
...newStatements, | ||
...changedSourceFile.statements, | ||
]); | ||
} | ||
} | ||
exports.default = CommonJsTransformer; | ||
//# sourceMappingURL=CommonJsTransformer.js.map |
import TypeScript from 'typescript'; | ||
export default class ResolveTransformer implements TypeScript.CustomTransformer { | ||
private context; | ||
private moduleResolutionHost; | ||
constructor(context: TypeScript.TransformationContext); | ||
private resolveDependencyName; | ||
private resolveDependencyPath; | ||
transformSourceFile(sourceFile: TypeScript.SourceFile): TypeScript.SourceFile; | ||
transformBundle(node: TypeScript.Bundle): TypeScript.Bundle; | ||
private resolveStaticImport; | ||
private resolveDynamicImport; | ||
private visit; | ||
transformSourceFile(node: TypeScript.SourceFile): TypeScript.SourceFile; | ||
transformBundle(node: TypeScript.Bundle): TypeScript.Bundle; | ||
} |
@@ -7,58 +7,58 @@ "use strict"; | ||
const typescript_1 = __importDefault(require("typescript")); | ||
const path_1 = __importDefault(require("path")); | ||
/* | ||
class ModuleResolutionHost implements TypeScript.ModuleResolutionHost { | ||
fileExists(fileName: string): boolean | ||
readFile(fileName: string): string | undefined | ||
trace?(s: string): void | ||
directoryExists?(directoryName: string): boolean | ||
realpath?(path: string): string | ||
getCurrentDirectory?(): string | ||
getDirectories?(path: string): string[] | ||
} | ||
*/ | ||
const DependencyResolver_1 = __importDefault(require("./utils/DependencyResolver")); | ||
class ResolveTransformer { | ||
constructor(context) { | ||
this.context = context; | ||
this.moduleResolutionHost = typescript_1.default.createCompilerHost(this.context.getCompilerOptions()); | ||
} | ||
// Return e.g. ./hello/module.js | ||
resolveDependencyName(parentPath, dendencyName) { | ||
const absolutePath = this.resolveDependencyPath(parentPath, dendencyName); | ||
const pathObj = path_1.default.parse(absolutePath); | ||
const relativeDir = path_1.default.relative(path_1.default.dirname(parentPath), pathObj.dir) || '.'; | ||
return relativeDir + '/' + pathObj.name + pathObj.ext; | ||
transformSourceFile(sourceFile) { | ||
const dynamicImportsResolved = this.resolveDynamicImport(sourceFile); | ||
return this.resolveStaticImport(dynamicImportsResolved); | ||
} | ||
// Return an aboslute path e.g. /tmp/a-apath/node_modules/hello/module.js | ||
resolveDependencyPath(parentPath, dendencyName) { | ||
const resolveResults = typescript_1.default.resolveModuleName(dendencyName, parentPath, this.context.getCompilerOptions(), this.moduleResolutionHost); | ||
if (resolveResults?.resolvedModule?.isExternalLibraryImport) { | ||
const nodeResolve = require.resolve(dendencyName, { | ||
paths: [path_1.default.dirname(parentPath)], | ||
}); | ||
if (nodeResolve) { | ||
// disable-eslint no-console | ||
console.error(nodeResolve); | ||
return nodeResolve; | ||
transformBundle(node) { | ||
return node; | ||
} | ||
resolveStaticImport(sourceFile) { | ||
const visit = (node) => { | ||
if ((typescript_1.default.isImportDeclaration(node) || | ||
typescript_1.default.isExportDeclaration(node)) && | ||
node.moduleSpecifier) { | ||
return typescript_1.default.visitEachChild(node, visit, this.context); | ||
} | ||
} | ||
const resolvedFileName = resolveResults?.resolvedModule?.resolvedFileName; | ||
if (!resolvedFileName) { | ||
throw new Error('Could not resolve' + dendencyName + 'from module' + parentPath); | ||
} | ||
return resolvedFileName; | ||
if (typescript_1.default.isStringLiteral(node) && | ||
node.parent && | ||
(typescript_1.default.isExportDeclaration(node.parent) || | ||
typescript_1.default.isImportDeclaration(node.parent)) && | ||
node.parent.moduleSpecifier) { | ||
return typescript_1.default.factory.createStringLiteral(new DependencyResolver_1.default(node.getSourceFile().fileName, this.context).resolveRelativeDependency(node.text)); | ||
} | ||
return node; | ||
}; | ||
return typescript_1.default.visitEachChild(sourceFile, visit, this.context); | ||
} | ||
resolveDynamicImport(sourceFile) { | ||
const visit = (node) => { | ||
if (node.parent && | ||
typescript_1.default.isCallExpression(node.parent) && | ||
node.parent.expression.kind === typescript_1.default.SyntaxKind.ImportKeyword && | ||
typescript_1.default.isStringLiteral(node) && | ||
node === node.parent.arguments[0]) { | ||
return typescript_1.default.factory.createStringLiteral(new DependencyResolver_1.default(node.getSourceFile().fileName, this.context).resolveRelativeDependency(node.text)); | ||
} | ||
return typescript_1.default.visitEachChild(node, visit, this.context); | ||
}; | ||
return typescript_1.default.visitEachChild(sourceFile, visit, this.context); | ||
} | ||
visit(node) { | ||
if (node.parent && | ||
typescript_1.default.isStringLiteral(node) && | ||
if ((typescript_1.default.isImportDeclaration(node) || | ||
typescript_1.default.isExportDeclaration(node)) && | ||
node.moduleSpecifier) { | ||
return typescript_1.default.visitEachChild(node, this.visit.bind(this), this.context); | ||
} | ||
if (typescript_1.default.isStringLiteral(node) && | ||
node.parent && | ||
(typescript_1.default.isExportDeclaration(node.parent) || | ||
typescript_1.default.isImportDeclaration(node.parent))) { | ||
return typescript_1.default.factory.createStringLiteral(this.resolveDependencyName(node.getSourceFile().fileName, node.text)); | ||
typescript_1.default.isImportDeclaration(node.parent)) && | ||
node.parent.moduleSpecifier) { | ||
return typescript_1.default.factory.createStringLiteral(new DependencyResolver_1.default(node.getSourceFile().fileName, this.context).resolveRelativeDependency(node.text)); | ||
} | ||
return typescript_1.default.visitEachChild(node, this.visit.bind(this), this.context); | ||
} | ||
transformSourceFile(node) { | ||
return typescript_1.default.visitNode(node, this.visit.bind(this)); | ||
} | ||
transformBundle(node) { | ||
return node; | ||
@@ -65,0 +65,0 @@ } |
@@ -1,5 +0,5 @@ | ||
/// <reference types="node" /> | ||
import TypeScript from 'typescript'; | ||
export default class TsTranspiler { | ||
transformCode(code: string, fileName: string): Promise<Buffer | string>; | ||
transformFile(fileName: string): Promise<Buffer | string>; | ||
transformCode(code: string, fileName: string): Promise<TypeScript.TranspileOutput>; | ||
transformFile(fileName: string): Promise<TypeScript.TranspileOutput>; | ||
} |
@@ -10,2 +10,4 @@ "use strict"; | ||
const CommonJsTransformer_1 = __importDefault(require("./transformers/CommonJsTransformer")); | ||
const NodeEnvTransformer_1 = __importDefault(require("./transformers/NodeEnvTransformer")); | ||
const CodeOptimizerTransformer_1 = __importDefault(require("./transformers/CodeOptimizerTransformer")); | ||
const compilerOptions = { | ||
@@ -21,5 +23,5 @@ allowJs: true, | ||
checkJs: false, | ||
noResolve: false, | ||
noResolve: true, | ||
esModuleInterop: true, | ||
skipLibCheck: true, | ||
skipLibCheck: false, | ||
target: typescript_1.default.ScriptTarget.ES2020, | ||
@@ -32,2 +34,6 @@ declaration: false, | ||
const transformers = { | ||
before: [ | ||
(context) => new NodeEnvTransformer_1.default(context), | ||
(context) => new CodeOptimizerTransformer_1.default(context), | ||
], | ||
after: [ | ||
@@ -40,10 +46,12 @@ (context) => new CommonJsTransformer_1.default(context), | ||
async transformCode(code, fileName) { | ||
const results = await typescript_1.default.transpileModule(code, { | ||
const results = typescript_1.default.transpileModule(code, { | ||
compilerOptions: compilerOptions, | ||
fileName: fileName, | ||
// reportDiagnostics: true, | ||
// renamedDependencies: {}, | ||
reportDiagnostics: true, | ||
transformers: transformers, | ||
}); | ||
return results.outputText; | ||
if (results.diagnostics?.length) { | ||
console.log(results.diagnostics); | ||
} | ||
return results; | ||
} | ||
@@ -50,0 +58,0 @@ async transformFile(fileName) { |
{ | ||
"name": "@ts-liveserver/ts-transpiler", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"main": "dist/index.js", | ||
@@ -5,0 +5,0 @@ "scripts": { |
import TypeScript from 'typescript' | ||
const KEYNAME_EXPORTS = 'exports' | ||
/* | ||
@@ -9,100 +10,393 @@ Transpile CommonJS to ES6 module | ||
private context: TypeScript.TransformationContext | ||
private counter = 0 | ||
constructor(context: TypeScript.TransformationContext) { | ||
this.context = context | ||
} | ||
private visit(node: TypeScript.Node) { | ||
// Import | ||
if ( | ||
TypeScript.isExpressionStatement(node) && | ||
TypeScript.isCallExpression(node.expression) && | ||
TypeScript.isIdentifier(node.expression.expression) && | ||
node.expression.expression.getText() === 'require' && | ||
node.expression.arguments.length === 1 | ||
) { | ||
const argument = node.expression.arguments[0] | ||
if (TypeScript.isStringLiteralLike(argument)) { | ||
return TypeScript.factory.createImportDeclaration( | ||
undefined, | ||
undefined, | ||
undefined, | ||
TypeScript.factory.createStringLiteral(argument.text), | ||
) | ||
transformBundle(): TypeScript.Bundle { | ||
throw new Error('Method not implemented.') | ||
} | ||
public transformSourceFile( | ||
sourceFile: TypeScript.SourceFile, | ||
): TypeScript.SourceFile { | ||
const requireInTopScope = this.requireTopScope(sourceFile) | ||
const withoutModule = this.stripModule(requireInTopScope) | ||
const withoutDefineProperty = this.convertDefinePropery(withoutModule) | ||
const esmExport = this.convertToEsmExport(withoutDefineProperty) | ||
return this.convertToEsmImport(esmExport) | ||
} | ||
// Generate a file unique variable name | ||
private generateUniqueName() { | ||
this.counter++ | ||
return 'GENERATED_VAR_BY_TRANSFORMER_' + String(this.counter) | ||
} | ||
// Convert all Object.defineProperty(exports, "hello", Hello);" | ||
private convertDefinePropery( | ||
sourceFile: TypeScript.SourceFile, | ||
): TypeScript.SourceFile { | ||
const visit = (node: TypeScript.Node): TypeScript.Node => { | ||
if ( | ||
TypeScript.isCallExpression(node) && | ||
TypeScript.isPropertyAccessExpression(node.expression) && | ||
TypeScript.isIdentifier(node.expression.expression) && | ||
TypeScript.isIdentifier(node.expression.name) && | ||
node.expression.expression.text === 'Object' && | ||
node.expression.name.text === 'defineProperty' && | ||
TypeScript.isIdentifier(node.arguments[0]) | ||
) { | ||
const firstArgument = node.arguments[0] | ||
const secondArgument = node.arguments[1] | ||
const thirdArgument = node.arguments[2] | ||
if ( | ||
TypeScript.isIdentifier(firstArgument) && | ||
firstArgument.text === KEYNAME_EXPORTS && | ||
TypeScript.isStringLiteral(secondArgument) | ||
) { | ||
if (TypeScript.isIdentifier(thirdArgument)) { | ||
return TypeScript.factory.createBinaryExpression( | ||
TypeScript.factory.createPropertyAccessExpression( | ||
TypeScript.factory.createIdentifier(KEYNAME_EXPORTS), | ||
TypeScript.factory.createIdentifier(secondArgument.text), | ||
), | ||
TypeScript.SyntaxKind.EqualsToken, | ||
thirdArgument, | ||
) | ||
} else if (TypeScript.isObjectLiteralExpression(thirdArgument)) { | ||
const propertyGetter = thirdArgument.properties | ||
.filter((property) => TypeScript.isPropertyAssignment(property)) | ||
.find( | ||
(property) => | ||
TypeScript.isPropertyAssignment(property) && | ||
TypeScript.isIdentifier(property.name) && | ||
property.name.text === 'get', | ||
) | ||
if ( | ||
propertyGetter && | ||
TypeScript.isPropertyAssignment(propertyGetter) && | ||
propertyGetter.initializer && | ||
TypeScript.isFunctionExpression(propertyGetter.initializer) && | ||
TypeScript.isBlock(propertyGetter.initializer.body) | ||
) { | ||
const firstStatement = | ||
propertyGetter.initializer.body.statements[0] | ||
if ( | ||
TypeScript.isReturnStatement(firstStatement) && | ||
firstStatement.expression | ||
) { | ||
return TypeScript.factory.createBinaryExpression( | ||
TypeScript.factory.createPropertyAccessExpression( | ||
TypeScript.factory.createIdentifier(KEYNAME_EXPORTS), | ||
TypeScript.factory.createIdentifier(secondArgument.text), | ||
), | ||
TypeScript.SyntaxKind.EqualsToken, | ||
firstStatement.expression, | ||
) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return TypeScript.visitEachChild(node, visit, this.context) | ||
} | ||
// Default import | ||
if ( | ||
TypeScript.isVariableStatement(node) && | ||
TypeScript.isVariableDeclarationList(node.declarationList) && | ||
node.declarationList.declarations.length === 1 && | ||
TypeScript.isVariableDeclaration(node.declarationList.declarations[0]) | ||
) { | ||
const declaration = node.declarationList.declarations[0] | ||
return TypeScript.visitNode(sourceFile, visit) | ||
} | ||
// Top level CommonJS to ESM | ||
private convertToEsmImport( | ||
sourceFile: TypeScript.SourceFile, | ||
): TypeScript.SourceFile { | ||
const visit = ( | ||
node: TypeScript.Node, | ||
): TypeScript.Node | TypeScript.Node[] => { | ||
// Import without name | ||
if ( | ||
TypeScript.isVariableDeclaration(declaration) && | ||
declaration.initializer && | ||
TypeScript.isCallExpression(declaration.initializer) && | ||
TypeScript.isIdentifier(declaration.initializer.expression) && | ||
declaration.initializer.expression.getText() === 'require' && | ||
declaration.initializer.arguments.length === 1 | ||
TypeScript.isExpressionStatement(node) && | ||
TypeScript.isCallExpression(node.expression) && | ||
TypeScript.isIdentifier(node.expression.expression) && | ||
node.expression.expression.text === 'require' && | ||
node.expression.arguments.length === 1 | ||
) { | ||
const argument = declaration.initializer.arguments[0] | ||
const argument = node.expression.arguments[0] | ||
if (TypeScript.isStringLiteralLike(argument)) { | ||
if (TypeScript.isIdentifier(declaration.name)) { | ||
return TypeScript.factory.createImportDeclaration( | ||
return TypeScript.factory.createImportDeclaration( | ||
undefined, | ||
undefined, | ||
undefined, | ||
TypeScript.factory.createStringLiteral(argument.text), | ||
) | ||
} | ||
} | ||
// Import with reference | ||
if ( | ||
TypeScript.isVariableStatement(node) && | ||
TypeScript.isVariableDeclarationList(node.declarationList) | ||
) { | ||
const importDeclarations: TypeScript.ImportDeclaration[] = [] | ||
const variableDeclarations: TypeScript.VariableDeclaration[] = [] | ||
for (const variableDeclaration of node.declarationList.declarations) { | ||
if ( | ||
variableDeclaration.initializer && | ||
TypeScript.isCallExpression(variableDeclaration.initializer) && | ||
TypeScript.isIdentifier( | ||
variableDeclaration.initializer.expression, | ||
) && | ||
variableDeclaration.initializer.expression.text === 'require' && | ||
variableDeclaration.initializer.arguments.length === 1 | ||
) { | ||
const argument = variableDeclaration.initializer.arguments[0] | ||
if ( | ||
TypeScript.isStringLiteral(argument) && | ||
TypeScript.isIdentifier(variableDeclaration.name) | ||
) { | ||
importDeclarations.push( | ||
TypeScript.factory.createImportDeclaration( | ||
undefined, | ||
undefined, | ||
TypeScript.factory.createImportClause( | ||
false, | ||
undefined, | ||
TypeScript.factory.createNamespaceImport( | ||
variableDeclaration.name, | ||
), | ||
), | ||
argument, | ||
), | ||
) | ||
} else if ( | ||
TypeScript.isStringLiteral(argument) && | ||
TypeScript.isObjectBindingPattern(variableDeclaration.name) | ||
) { | ||
importDeclarations.push( | ||
TypeScript.factory.createImportDeclaration( | ||
undefined, | ||
undefined, | ||
TypeScript.factory.createImportClause( | ||
false, | ||
undefined, | ||
TypeScript.factory.createNamedImports( | ||
variableDeclaration.name.elements.map((bindingElement) => | ||
TypeScript.factory.createImportSpecifier( | ||
bindingElement.propertyName as TypeScript.Identifier, | ||
bindingElement.name as TypeScript.Identifier, | ||
), | ||
), | ||
), | ||
), | ||
TypeScript.factory.createStringLiteral(argument.text), | ||
), | ||
) | ||
} | ||
} else { | ||
variableDeclarations.push(variableDeclaration) | ||
} | ||
} | ||
if (variableDeclarations.length === 0) { | ||
return importDeclarations | ||
} else { | ||
return [ | ||
...importDeclarations, | ||
TypeScript.factory.updateVariableStatement( | ||
node, | ||
undefined, | ||
TypeScript.factory.updateVariableDeclarationList( | ||
node.declarationList, | ||
variableDeclarations, | ||
), | ||
), | ||
] | ||
} | ||
} | ||
return node | ||
} | ||
return TypeScript.visitEachChild(sourceFile, visit, this.context) | ||
} | ||
// Top level CommonJS to ESM | ||
private convertToEsmExport( | ||
sourceFile: TypeScript.SourceFile, | ||
): TypeScript.SourceFile { | ||
const visit = ( | ||
node: TypeScript.Node, | ||
): TypeScript.Node | TypeScript.Node[] => { | ||
if ( | ||
TypeScript.isExpressionStatement(node) && | ||
TypeScript.isBinaryExpression(node.expression) && | ||
node.expression.operatorToken.kind === TypeScript.SyntaxKind.EqualsToken | ||
) { | ||
// exports.something = something; | ||
if ( | ||
TypeScript.isPropertyAccessExpression(node.expression.left) && | ||
TypeScript.isIdentifier(node.expression.left.name) && | ||
TypeScript.isIdentifier(node.expression.left.expression) && | ||
node.expression.left.expression.text === KEYNAME_EXPORTS | ||
) { | ||
// exports.default = something; | ||
if (node.expression.left.name.text === 'default') { | ||
return TypeScript.factory.createExportAssignment( | ||
undefined, | ||
TypeScript.factory.createImportClause( | ||
false, | ||
declaration.name, | ||
undefined, | ||
), | ||
TypeScript.factory.createStringLiteral(argument.text), | ||
undefined, | ||
undefined, | ||
node.expression.right, | ||
) | ||
} else if (TypeScript.isObjectBindingPattern(declaration.name)) { | ||
return TypeScript.factory.createImportDeclaration( | ||
} | ||
// exports.something = anIdentifier; | ||
else if (TypeScript.isIdentifier(node.expression.right)) { | ||
return TypeScript.factory.createExportDeclaration( | ||
undefined, | ||
undefined, | ||
TypeScript.factory.createImportClause( | ||
false, | ||
TypeScript.factory.createNamedExports([ | ||
TypeScript.factory.createExportSpecifier( | ||
node.expression.right.text === node.expression.left.name.text | ||
? undefined | ||
: node.expression.right, | ||
node.expression.left.name, | ||
), | ||
]), | ||
) | ||
} else { | ||
const newIdentifierName = this.generateUniqueName() | ||
const variableStatement = TypeScript.factory.createVariableStatement( | ||
undefined, | ||
[ | ||
TypeScript.factory.createVariableDeclaration( | ||
newIdentifierName, | ||
undefined, | ||
undefined, | ||
node.expression.right, | ||
), | ||
], | ||
) | ||
return [ | ||
variableStatement, | ||
TypeScript.factory.createExportDeclaration( | ||
undefined, | ||
undefined, | ||
false, | ||
undefined, | ||
TypeScript.factory.createNamedImports( | ||
declaration.name.elements.map((bindingElement) => | ||
TypeScript.factory.createImportSpecifier( | ||
bindingElement.propertyName as TypeScript.Identifier, | ||
bindingElement.name as TypeScript.Identifier, | ||
), | ||
TypeScript.factory.createNamedExports([ | ||
TypeScript.factory.createExportSpecifier( | ||
TypeScript.factory.createIdentifier(newIdentifierName), | ||
node.expression.left.name, | ||
), | ||
), | ||
]), | ||
), | ||
TypeScript.factory.createStringLiteral(argument.text), | ||
] | ||
} | ||
} | ||
// exports = something; | ||
if ( | ||
TypeScript.isIdentifier(node.expression.left) && | ||
node.expression.left.text === KEYNAME_EXPORTS | ||
) { | ||
// exports = { a: false, b: true } | ||
if (TypeScript.isObjectLiteralExpression(node.expression.right)) { | ||
const exportSpecifiers: TypeScript.ExportSpecifier[] = [] | ||
for (const property of node.expression.right.properties) { | ||
if ( | ||
TypeScript.isPropertyAssignment(property) && | ||
TypeScript.isIdentifier(property.name) && | ||
TypeScript.isIdentifier(property.initializer) | ||
) { | ||
exportSpecifiers.push( | ||
TypeScript.factory.createExportSpecifier( | ||
property.initializer.text === property.name.text | ||
? undefined | ||
: property.initializer, | ||
property.name, | ||
), | ||
) | ||
} | ||
} | ||
return TypeScript.factory.createExportDeclaration( | ||
undefined, | ||
undefined, | ||
false, | ||
TypeScript.factory.createNamedExports(exportSpecifiers), | ||
) | ||
} | ||
// exports = require('hello.js'); | ||
else if ( | ||
TypeScript.isCallExpression(node.expression.right) && | ||
TypeScript.isIdentifier(node.expression.right.expression) && | ||
node.expression.right.expression.text === 'require' && | ||
TypeScript.isStringLiteral(node.expression.right.arguments[0]) | ||
) { | ||
return TypeScript.factory.createExportDeclaration( | ||
undefined, | ||
undefined, | ||
false, | ||
undefined, | ||
node.expression.right.arguments[0], | ||
) | ||
} | ||
// exports = 'hello' or exports = Hello | ||
else { | ||
return TypeScript.factory.createExportAssignment( | ||
undefined, | ||
undefined, | ||
undefined, | ||
node.expression.right, | ||
) | ||
} | ||
} | ||
} | ||
return node | ||
} | ||
// Default export | ||
if ( | ||
TypeScript.isExpressionStatement(node) && | ||
TypeScript.isBinaryExpression(node.expression) && | ||
TypeScript.isPropertyAccessExpression(node.expression.left) && | ||
TypeScript.isIdentifier(node.expression.left.expression) && | ||
TypeScript.isIdentifier(node.expression.left.name) && | ||
node.expression.left.name.getText() === 'exports' && | ||
node.expression.left.expression.getText() === 'module' | ||
) { | ||
return TypeScript.factory.createExportAssignment( | ||
undefined, | ||
undefined, | ||
undefined, | ||
node.expression.right, | ||
) | ||
return TypeScript.visitEachChild(sourceFile, visit, this.context) | ||
} | ||
// module.exports -> exports | ||
private stripModule( | ||
sourceFile: TypeScript.SourceFile, | ||
): TypeScript.SourceFile { | ||
const visit = (node: TypeScript.Node): TypeScript.Node => { | ||
if ( | ||
TypeScript.isPropertyAccessExpression(node) && | ||
TypeScript.isIdentifier(node.expression) && | ||
TypeScript.isIdentifier(node.name) && | ||
node.expression.text === 'module' && | ||
node.name.text === KEYNAME_EXPORTS | ||
) { | ||
return node.name | ||
} | ||
return TypeScript.visitEachChild(node, visit, this.context) | ||
} | ||
return node | ||
return TypeScript.visitNode(sourceFile, visit) | ||
} | ||
transformSourceFile(node: TypeScript.SourceFile): TypeScript.SourceFile { | ||
return TypeScript.visitEachChild(node, this.visit.bind(this), this.context) | ||
// Move all require-calls to top-scope | ||
private requireTopScope( | ||
sourceFile: TypeScript.SourceFile, | ||
): TypeScript.SourceFile { | ||
const newStatements: TypeScript.VariableStatement[] = [] | ||
const visit = (node: TypeScript.Node): TypeScript.Node => { | ||
const inRootScope = | ||
node?.parent?.parent?.parent?.parent && | ||
TypeScript.isVariableDeclaration(node.parent) && | ||
TypeScript.isVariableDeclarationList(node.parent.parent) && | ||
TypeScript.isVariableStatement(node.parent.parent.parent) && | ||
TypeScript.isSourceFile(node.parent.parent.parent.parent) | ||
if ( | ||
TypeScript.isCallExpression(node) && | ||
TypeScript.isIdentifier(node.expression) && | ||
node.arguments.length === 1 && | ||
node.expression.text === 'require' && | ||
inRootScope === false | ||
) { | ||
const newIdentifierName = this.generateUniqueName() | ||
newStatements.push( | ||
TypeScript.factory.createVariableStatement(undefined, [ | ||
TypeScript.factory.createVariableDeclaration( | ||
newIdentifierName, | ||
undefined, | ||
undefined, | ||
node, | ||
), | ||
]), | ||
) | ||
return TypeScript.factory.createIdentifier(newIdentifierName) | ||
} | ||
return TypeScript.visitEachChild(node, visit, this.context) | ||
} | ||
const changedSourceFile = TypeScript.visitNode(sourceFile, visit) | ||
return TypeScript.factory.updateSourceFile(changedSourceFile, [ | ||
...newStatements, | ||
...changedSourceFile.statements, | ||
]) | ||
} | ||
transformBundle(node: TypeScript.Bundle): TypeScript.Bundle { | ||
return node | ||
} | ||
} |
import TypeScript from 'typescript' | ||
import Path from 'path' | ||
/* | ||
class ModuleResolutionHost implements TypeScript.ModuleResolutionHost { | ||
fileExists(fileName: string): boolean | ||
readFile(fileName: string): string | undefined | ||
trace?(s: string): void | ||
directoryExists?(directoryName: string): boolean | ||
realpath?(path: string): string | ||
getCurrentDirectory?(): string | ||
getDirectories?(path: string): string[] | ||
} | ||
*/ | ||
import DependencyResolver from './utils/DependencyResolver' | ||
@@ -18,68 +7,95 @@ export default class ResolveTransformer | ||
private context: TypeScript.TransformationContext | ||
private moduleResolutionHost: TypeScript.ModuleResolutionHost | ||
constructor(context: TypeScript.TransformationContext) { | ||
this.context = context | ||
this.moduleResolutionHost = TypeScript.createCompilerHost( | ||
this.context.getCompilerOptions(), | ||
) | ||
} | ||
// Return e.g. ./hello/module.js | ||
private resolveDependencyName( | ||
parentPath: string, | ||
dendencyName: string, | ||
): string { | ||
const absolutePath = this.resolveDependencyPath(parentPath, dendencyName) | ||
const pathObj = Path.parse(absolutePath) | ||
const relativeDir = | ||
Path.relative(Path.dirname(parentPath), pathObj.dir) || '.' | ||
return relativeDir + '/' + pathObj.name + pathObj.ext | ||
public transformSourceFile( | ||
sourceFile: TypeScript.SourceFile, | ||
): TypeScript.SourceFile { | ||
const dynamicImportsResolved = this.resolveDynamicImport(sourceFile) | ||
return this.resolveStaticImport(dynamicImportsResolved) | ||
} | ||
// Return an aboslute path e.g. /tmp/a-apath/node_modules/hello/module.js | ||
private resolveDependencyPath( | ||
parentPath: string, | ||
dendencyName: string, | ||
): string { | ||
const resolveResults = TypeScript.resolveModuleName( | ||
dendencyName, | ||
parentPath, | ||
this.context.getCompilerOptions(), | ||
this.moduleResolutionHost, | ||
) | ||
if (resolveResults?.resolvedModule?.isExternalLibraryImport) { | ||
const nodeResolve = require.resolve(dendencyName, { | ||
paths: [Path.dirname(parentPath)], | ||
}) | ||
if (nodeResolve) { | ||
// disable-eslint no-console | ||
console.error(nodeResolve) | ||
return nodeResolve | ||
public transformBundle(node: TypeScript.Bundle): TypeScript.Bundle { | ||
return node | ||
} | ||
private resolveStaticImport( | ||
sourceFile: TypeScript.SourceFile, | ||
): TypeScript.SourceFile { | ||
const visit = (node: TypeScript.Node): TypeScript.Node => { | ||
if ( | ||
(TypeScript.isImportDeclaration(node) || | ||
TypeScript.isExportDeclaration(node)) && | ||
node.moduleSpecifier | ||
) { | ||
return TypeScript.visitEachChild(node, visit, this.context) | ||
} | ||
if ( | ||
TypeScript.isStringLiteral(node) && | ||
node.parent && | ||
(TypeScript.isExportDeclaration(node.parent) || | ||
TypeScript.isImportDeclaration(node.parent)) && | ||
node.parent.moduleSpecifier | ||
) { | ||
return TypeScript.factory.createStringLiteral( | ||
new DependencyResolver( | ||
node.getSourceFile().fileName, | ||
this.context, | ||
).resolveRelativeDependency(node.text), | ||
// this.resolveDependencyName(node.getSourceFile().fileName, node.text), | ||
) | ||
} | ||
return node | ||
} | ||
const resolvedFileName = resolveResults?.resolvedModule?.resolvedFileName | ||
if (!resolvedFileName) { | ||
throw new Error( | ||
'Could not resolve' + dendencyName + 'from module' + parentPath, | ||
) | ||
return TypeScript.visitEachChild(sourceFile, visit, this.context) | ||
} | ||
private resolveDynamicImport( | ||
sourceFile: TypeScript.SourceFile, | ||
): TypeScript.SourceFile { | ||
const visit = (node: TypeScript.Node): TypeScript.Node => { | ||
if ( | ||
node.parent && | ||
TypeScript.isCallExpression(node.parent) && | ||
node.parent.expression.kind === TypeScript.SyntaxKind.ImportKeyword && | ||
TypeScript.isStringLiteral(node) && | ||
node === node.parent.arguments[0] | ||
) { | ||
return TypeScript.factory.createStringLiteral( | ||
new DependencyResolver( | ||
node.getSourceFile().fileName, | ||
this.context, | ||
).resolveRelativeDependency(node.text), | ||
) | ||
} | ||
return TypeScript.visitEachChild(node, visit, this.context) | ||
} | ||
return resolvedFileName | ||
return TypeScript.visitEachChild(sourceFile, visit, this.context) | ||
} | ||
private visit(node: TypeScript.Node) { | ||
if ( | ||
(TypeScript.isImportDeclaration(node) || | ||
TypeScript.isExportDeclaration(node)) && | ||
node.moduleSpecifier | ||
) { | ||
return TypeScript.visitEachChild( | ||
node, | ||
this.visit.bind(this), | ||
this.context, | ||
) | ||
} | ||
if ( | ||
TypeScript.isStringLiteral(node) && | ||
node.parent && | ||
TypeScript.isStringLiteral(node) && | ||
(TypeScript.isExportDeclaration(node.parent) || | ||
TypeScript.isImportDeclaration(node.parent)) | ||
TypeScript.isImportDeclaration(node.parent)) && | ||
node.parent.moduleSpecifier | ||
) { | ||
return TypeScript.factory.createStringLiteral( | ||
this.resolveDependencyName(node.getSourceFile().fileName, node.text), | ||
new DependencyResolver( | ||
node.getSourceFile().fileName, | ||
this.context, | ||
).resolveRelativeDependency(node.text), | ||
// this.resolveDependencyName(node.getSourceFile().fileName, node.text), | ||
) | ||
} | ||
return TypeScript.visitEachChild(node, this.visit.bind(this), this.context) | ||
} | ||
transformSourceFile(node: TypeScript.SourceFile): TypeScript.SourceFile { | ||
return TypeScript.visitNode(node, this.visit.bind(this)) | ||
} | ||
transformBundle(node: TypeScript.Bundle): TypeScript.Bundle { | ||
return node | ||
} | ||
} |
@@ -5,2 +5,4 @@ import Fs from 'fs' | ||
import CommonJsTransformer from './transformers/CommonJsTransformer' | ||
import NodeEnvTransformer from './transformers/NodeEnvTransformer' | ||
import CodeOptimizerTransformer from './transformers/CodeOptimizerTransformer' | ||
@@ -17,5 +19,5 @@ const compilerOptions: TypeScript.CompilerOptions = { | ||
checkJs: false, | ||
noResolve: false, | ||
noResolve: true, | ||
esModuleInterop: true, | ||
skipLibCheck: true, | ||
skipLibCheck: false, | ||
target: TypeScript.ScriptTarget.ES2020, | ||
@@ -29,2 +31,6 @@ declaration: false, | ||
const transformers: TypeScript.CustomTransformers = { | ||
before: [ | ||
(context) => new NodeEnvTransformer(context), | ||
(context) => new CodeOptimizerTransformer(context), | ||
], | ||
after: [ | ||
@@ -40,13 +46,15 @@ (context) => new CommonJsTransformer(context), | ||
fileName: string, | ||
): Promise<Buffer | string> { | ||
const results = await TypeScript.transpileModule(code, { | ||
): Promise<TypeScript.TranspileOutput> { | ||
const results = TypeScript.transpileModule(code, { | ||
compilerOptions: compilerOptions, | ||
fileName: fileName, | ||
// reportDiagnostics: true, | ||
// renamedDependencies: {}, | ||
reportDiagnostics: true, | ||
transformers: transformers, | ||
}) | ||
return results.outputText | ||
if (results.diagnostics?.length) { | ||
console.log(results.diagnostics) | ||
} | ||
return results | ||
} | ||
async transformFile(fileName: string): Promise<Buffer | string> { | ||
async transformFile(fileName: string): Promise<TypeScript.TranspileOutput> { | ||
const buffer = await Fs.promises.readFile(fileName) | ||
@@ -53,0 +61,0 @@ return this.transformCode(buffer.toString(), fileName) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
98080
53
1929
5
1