Skip to content

Commit

Permalink
Move transformations to module, rewrite dynamic import
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewbranch committed Sep 17, 2024
1 parent 4867001 commit 62dab48
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 67 deletions.
75 changes: 65 additions & 10 deletions src/compiler/transformers/module/esnextAnd2015.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
hasSyntacticModifier,
Identifier,
idText,
ImportCall,
ImportDeclaration,
ImportEqualsDeclaration,
insertStatementsAfterCustomPrologue,
Expand All @@ -31,17 +32,22 @@ import {
isExternalModuleImportEqualsDeclaration,
isExternalModuleIndicator,
isIdentifier,
isImportCall,
isNamespaceExport,
isSourceFile,
isStatement,
mapDefined,
ModifierFlags,
ModuleKind,
Node,
NodeFlags,
NodeId,
rangeContainsRange,
rewriteModuleSpecifier,
ScriptTarget,
setOriginalNode,
setTextRange,
shouldRewriteModuleSpecifier,
singleOrMany,
some,
SourceFile,
Expand Down Expand Up @@ -73,6 +79,7 @@ export function transformECMAScriptModule(context: TransformationContext): (x: S
context.enableSubstitution(SyntaxKind.Identifier);

const noSubstitution = new Set<NodeId>();
let importCallsToRewrite: ImportCall[] | undefined;
let helperNameSubstitutions: Map<string, Identifier> | undefined;
let currentSourceFile: SourceFile | undefined;
let importRequireStatements: [ImportDeclaration, VariableStatement] | undefined;
Expand All @@ -86,6 +93,9 @@ export function transformECMAScriptModule(context: TransformationContext): (x: S
if (isExternalModule(node) || getIsolatedModules(compilerOptions)) {
currentSourceFile = node;
importRequireStatements = undefined;
importCallsToRewrite = compilerOptions.rewriteRelativeImportExtensions
? mapDefined(node.imports, name => isImportCall(name.parent) && shouldRewriteModuleSpecifier(name.text, compilerOptions) ? name.parent : undefined)
: undefined;
let result = updateExternalModule(node);
currentSourceFile = undefined;
if (importRequireStatements) {
Expand Down Expand Up @@ -135,11 +145,49 @@ export function transformECMAScriptModule(context: TransformationContext): (x: S
case SyntaxKind.ExportDeclaration:
const exportDecl = node as ExportDeclaration;
return visitExportDeclaration(exportDecl);
case SyntaxKind.ImportDeclaration:
return visitImportDeclaration(node as ImportDeclaration);
case SyntaxKind.CallExpression:
if (node === importCallsToRewrite?.[0]) {
importCallsToRewrite.shift();
return visitImportCall(node as ImportCall);
}
break;
default:
if (importCallsToRewrite?.length && rangeContainsRange(node, importCallsToRewrite[0])) {
return visitEachChild(node, visitor, context);
}
}

return node;
}

function visitImportDeclaration(node: ImportDeclaration): VisitResult<ImportDeclaration> {
if (!compilerOptions.rewriteRelativeImportExtensions) {
return node;
}
const updatedModuleSpecifier = rewriteModuleSpecifier(node.moduleSpecifier, compilerOptions);
if (updatedModuleSpecifier === node.moduleSpecifier) {
return node;
}
return factory.updateImportDeclaration(
node,
node.modifiers,
node.importClause,
updatedModuleSpecifier,
node.attributes,
);
}

function visitImportCall(node: ImportCall): VisitResult<Expression> {
return factory.updateCallExpression(
node,
node.expression,
node.typeArguments,
[rewriteModuleSpecifier(node.arguments[0], compilerOptions), ...node.arguments.slice(1)],
);
}

/**
* Creates a `require()` call to import an external module.
*
Expand All @@ -149,7 +197,7 @@ export function transformECMAScriptModule(context: TransformationContext): (x: S
const moduleName = getExternalModuleNameLiteral(factory, importNode, Debug.checkDefined(currentSourceFile), host, resolver, compilerOptions);
const args: Expression[] = [];
if (moduleName) {
args.push(moduleName);
args.push(rewriteModuleSpecifier(moduleName, compilerOptions));
}
if (getEmitModuleKind(compilerOptions) === ModuleKind.Preserve) {
return factory.createCallExpression(factory.createIdentifier("require"), /*typeArguments*/ undefined, args);
Expand Down Expand Up @@ -270,14 +318,21 @@ export function transformECMAScriptModule(context: TransformationContext): (x: S
}

function visitExportDeclaration(node: ExportDeclaration) {
// `export * as ns` only needs to be transformed in ES2015
if (compilerOptions.module !== undefined && compilerOptions.module > ModuleKind.ES2015) {
return node;
}

// Either ill-formed or don't need to be tranformed.
if (!node.exportClause || !isNamespaceExport(node.exportClause) || !node.moduleSpecifier) {
return node;
const updatedModuleSpecifier = rewriteModuleSpecifier(node.moduleSpecifier, compilerOptions);
if (
(compilerOptions.module !== undefined && compilerOptions.module > ModuleKind.ES2015)
|| !node.exportClause || !isNamespaceExport(node.exportClause) || !node.moduleSpecifier
) {
// Either ill-formed or don't need to be tranformed.
return (!node.moduleSpecifier || updatedModuleSpecifier === node.moduleSpecifier) ? node :
factory.updateExportDeclaration(
node,
node.modifiers,
node.isTypeOnly,
node.exportClause,
updatedModuleSpecifier,
node.attributes,
);
}

const oldIdentifier = node.exportClause.name;
Expand All @@ -291,7 +346,7 @@ export function transformECMAScriptModule(context: TransformationContext): (x: S
synthName,
),
),
node.moduleSpecifier,
updatedModuleSpecifier!,
node.attributes,
);
setOriginalNode(importDecl, node.exportClause);
Expand Down
7 changes: 5 additions & 2 deletions src/compiler/transformers/module/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ import {
PrefixUnaryExpression,
reduceLeft,
removeAllComments,
rewriteModuleSpecifier,
ScriptTarget,
setEmitFlags,
setOriginalNode,
Expand Down Expand Up @@ -1177,7 +1178,9 @@ export function transformModule(context: TransformationContext): (x: SourceFile
const externalModuleName = getExternalModuleNameLiteral(factory, node, currentSourceFile, host, resolver, compilerOptions);
const firstArgument = visitNode(firstOrUndefined(node.arguments), visitor, isExpression);
// Only use the external module name if it differs from the first argument. This allows us to preserve the quote style of the argument on output.
const argument = externalModuleName && (!firstArgument || !isStringLiteral(firstArgument) || firstArgument.text !== externalModuleName.text) ? externalModuleName : firstArgument;
const argument = externalModuleName && (!firstArgument || !isStringLiteral(firstArgument) || firstArgument.text !== externalModuleName.text)
? externalModuleName
: rewriteModuleSpecifier(firstArgument, compilerOptions);
const containsLexicalThis = !!(node.transformFlags & TransformFlags.ContainsLexicalThis);
switch (compilerOptions.module) {
case ModuleKind.AMD:
Expand Down Expand Up @@ -1500,7 +1503,7 @@ export function transformModule(context: TransformationContext): (x: SourceFile
const moduleName = getExternalModuleNameLiteral(factory, importNode, currentSourceFile, host, resolver, compilerOptions);
const args: Expression[] = [];
if (moduleName) {
args.push(moduleName);
args.push(rewriteModuleSpecifier(moduleName, compilerOptions));
}

return factory.createCallExpression(factory.createIdentifier("require"), /*typeArguments*/ undefined, args);
Expand Down
43 changes: 4 additions & 39 deletions src/compiler/transformers/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
Bundle,
CallExpression,
CaseBlock,
changeExtension,
childIsDecorated,
ClassDeclaration,
ClassElement,
Expand Down Expand Up @@ -56,7 +55,6 @@ import {
getInitializedVariables,
getIsolatedModules,
getOriginalNode,
getOutputExtension,
getParseTreeNode,
getProperties,
getStrictOptionValue,
Expand Down Expand Up @@ -124,7 +122,6 @@ import {
isSimpleInlineableExpression,
isSourceFile,
isStatement,
isStringLiteral,
isTemplateLiteral,
isTryStatement,
JsxOpeningElement,
Expand Down Expand Up @@ -180,7 +177,6 @@ import {
setTypeNode,
ShorthandPropertyAssignment,
shouldPreserveConstEnums,
shouldRewriteModuleSpecifier,
skipOuterExpressions,
skipPartiallyEmittedExpressions,
skipTrivia,
Expand Down Expand Up @@ -2259,14 +2255,7 @@ export function transformTypeScript(context: TransformationContext) {
if (!node.importClause) {
// Do not elide a side-effect only import declaration.
// import "foo";
const specifier = rewriteModuleSpecifier(node.moduleSpecifier);
return specifier === node.moduleSpecifier ? node : factory.updateImportDeclaration(
node,
/*modifiers*/ undefined,
/*importClause*/ undefined,
specifier,
node.attributes,
);
return node;
}
if (node.importClause.isTypeOnly) {
// Always elide type-only imports
Expand All @@ -2280,22 +2269,12 @@ export function transformTypeScript(context: TransformationContext) {
node,
/*modifiers*/ undefined,
importClause,
rewriteModuleSpecifier(node.moduleSpecifier),
node.moduleSpecifier,
node.attributes,
)
: undefined;
}

function rewriteModuleSpecifier(node: Expression): Expression;
function rewriteModuleSpecifier(node: Expression | undefined): Expression | undefined;
function rewriteModuleSpecifier(node: Expression | undefined): Expression | undefined {
if (!node || !isStringLiteral(node) || !shouldRewriteModuleSpecifier(node.text, compilerOptions)) {
return node;
}
const updatedText = changeExtension(node.text, getOutputExtension(node.text, compilerOptions));
return updatedText !== node.text ? setOriginalNode(setTextRange(factory.createStringLiteral(updatedText, node.singleQuote), node), node) : node;
}

/**
* Visits an import clause, eliding it if its `name` and `namedBindings` may both be elided.
*
Expand Down Expand Up @@ -2368,7 +2347,7 @@ export function transformTypeScript(context: TransformationContext) {
node.modifiers,
node.isTypeOnly,
node.exportClause,
rewriteModuleSpecifier(node.moduleSpecifier),
node.moduleSpecifier,
node.attributes,
);
}
Expand All @@ -2387,7 +2366,7 @@ export function transformTypeScript(context: TransformationContext) {
/*modifiers*/ undefined,
node.isTypeOnly,
exportClause,
rewriteModuleSpecifier(node.moduleSpecifier),
node.moduleSpecifier,
node.attributes,
)
: undefined;
Expand Down Expand Up @@ -2452,20 +2431,6 @@ export function transformTypeScript(context: TransformationContext) {
if (!shouldEmitAliasDeclaration(node)) {
return undefined;
}
const updatedModuleSpecifier = rewriteModuleSpecifier(node.moduleReference.expression);
if (updatedModuleSpecifier !== node.moduleReference.expression) {
return visitEachChild(
factory.updateImportEqualsDeclaration(
node,
node.modifiers,
node.isTypeOnly,
node.name,
factory.updateExternalModuleReference(node.moduleReference, updatedModuleSpecifier),
),
visitor,
context,
);
}
return visitEachChild(node, visitor, context);
}

Expand Down
19 changes: 19 additions & 0 deletions src/compiler/transformers/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import {
BindingElement,
Bundle,
cast,
changeExtension,
ClassDeclaration,
ClassElement,
ClassExpression,
ClassLikeDeclaration,
ClassStaticBlockDeclaration,
CompilerOptions,
CompoundAssignmentOperator,
CoreTransformationContext,
createExternalHelpersImportDeclarationIfNeeded,
Expand All @@ -21,6 +23,7 @@ import {
ExportDeclaration,
ExportSpecifier,
Expression,
factory,
filter,
formatGeneratedName,
FunctionDeclaration,
Expand All @@ -33,6 +36,7 @@ import {
getNodeForGeneratedName,
getNodeId,
getOriginalNode,
getOutputExtension,
hasDecorators,
hasStaticModifier,
hasSyntacticModifier,
Expand Down Expand Up @@ -61,6 +65,7 @@ import {
isPrivateIdentifier,
isPropertyDeclaration,
isStatic,
isStringLiteral,
isStringLiteralLike,
isSuperCall,
isTryStatement,
Expand All @@ -83,6 +88,9 @@ import {
PrivateIdentifierAutoAccessorPropertyDeclaration,
PrivateIdentifierMethodDeclaration,
PropertyDeclaration,
setOriginalNode,
setTextRange,
shouldRewriteModuleSpecifier,
skipParentheses,
some,
SourceFile,
Expand Down Expand Up @@ -863,3 +871,14 @@ function isSimpleParameter(node: ParameterDeclaration) {
export function isSimpleParameterList(nodes: NodeArray<ParameterDeclaration>) {
return every(nodes, isSimpleParameter);
}

/** @internal */
export function rewriteModuleSpecifier(node: Expression, compilerOptions: CompilerOptions): Expression;
export function rewriteModuleSpecifier(node: Expression | undefined, compilerOptions: CompilerOptions): Expression | undefined;
export function rewriteModuleSpecifier(node: Expression | undefined, compilerOptions: CompilerOptions): Expression | undefined {
if (!node || !isStringLiteral(node) || !shouldRewriteModuleSpecifier(node.text, compilerOptions)) {
return node;
}
const updatedText = changeExtension(node.text, getOutputExtension(node.text, compilerOptions));
return updatedText !== node.text ? setOriginalNode(setTextRange(factory.createStringLiteral(updatedText, node.singleQuote), node), node) : node;
}
10 changes: 10 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7779,6 +7779,16 @@ export function getLinesBetweenPositionAndNextNonWhitespaceCharacter(pos: number
return getLinesBetweenPositions(sourceFile, pos, Math.min(stopPos, nextPos));
}

/** @internal */
export function rangeContainsRange(r1: TextRange, r2: TextRange): boolean {
return startEndContainsRange(r1.pos, r1.end, r2);
}

/** @internal */
export function startEndContainsRange(start: number, end: number, range: TextRange): boolean {
return start <= range.pos && end >= range.end;
}

function getPreviousNonWhitespacePosition(pos: number, stopPos = 0, sourceFile: SourceFile) {
while (pos-- > stopPos) {
if (!isWhiteSpaceLike(sourceFile.text.charCodeAt(pos))) {
Expand Down
11 changes: 1 addition & 10 deletions src/services/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ import {
PseudoBigInt,
pseudoBigIntToString,
QualifiedName,
rangeContainsRange,
RefactorContext,
removeFileExtension,
removeSuffix,
Expand Down Expand Up @@ -917,11 +918,6 @@ export function getLineStartPositionForPosition(position: number, sourceFile: So
return lineStarts[line];
}

/** @internal */
export function rangeContainsRange(r1: TextRange, r2: TextRange): boolean {
return startEndContainsRange(r1.pos, r1.end, r2);
}

/** @internal */
export function rangeContainsRangeExclusive(r1: TextRange, r2: TextRange): boolean {
return rangeContainsPositionExclusive(r1, r2.pos) && rangeContainsPositionExclusive(r1, r2.end);
Expand All @@ -937,11 +933,6 @@ export function rangeContainsPositionExclusive(r: TextRange, pos: number) {
return r.pos < pos && pos < r.end;
}

/** @internal */
export function startEndContainsRange(start: number, end: number, range: TextRange): boolean {
return start <= range.pos && end >= range.end;
}

/** @internal */
export function rangeContainsStartEnd(range: TextRange, start: number, end: number): boolean {
return range.pos <= start && range.end >= end;
Expand Down
Loading

0 comments on commit 62dab48

Please sign in to comment.