mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
--moduleResolution bundler (formerly known as hybrid) (#51669)
* WIP * Add extension error back unless noEmit is set * Add non-relative tests * Add error for importing from declaration file * Update unit test * Add explicit flag for importing from .ts extensions * Add module specifier resolution changes * Add auto-import tests * Disallow relative imports into node_modules * Ensure auto-imports don’t suggest ./node_modules; * Test a non-portable declaration emit issue * Test auto-importing TSX file * Update path completions * Fix lint due to merge * Remove minimal-specific stuff * Remove minimal tests * Update unit tests * Add options * Add customConditions option * Add first tests * CJS constructs are not allowed * Add another test * Fix extension adding/replacing priority * Update test to reflect the choice not to block on unrecognized extensions * Add auto-imports and string completions tests * Revamp string completions ending preferences * Comment test * Auto-imports of declaration files cannot use .ts extension * Have declaration file auto imports default to extensionless instead * Add test for custom conditions * Fix indentation * Add baseline showing resolvePackageJsonImports/Exports compatibility * Fix test and prevent CJS require from resolving * Update unit test baselines * Fix bad merge conflict resolution * Make resolvedUsingTsExtension optional * Update missed baselines * Revert now-unnecessary API implementation changes * Clean up * Update baselines to es5 emit * Rename to `bundler`
This commit is contained in:
@@ -804,18 +804,6 @@ export class TestState {
|
||||
});
|
||||
}
|
||||
|
||||
private renderMarkers(markers: { text: string, fileName: string, position: number }[]) {
|
||||
const filesToDisplay = ts.deduplicate(markers.map(m => m.fileName), ts.equateValues);
|
||||
return filesToDisplay.map(fileName => {
|
||||
const markersToRender = markers.filter(m => m.fileName === fileName).sort((a, b) => b.position - a.position);
|
||||
let fileContent = this.tryGetFileContent(fileName) || "";
|
||||
for (const marker of markersToRender) {
|
||||
fileContent = fileContent.slice(0, marker.position) + `\x1b[1;4m/*${marker.text}*/\x1b[0;31m` + fileContent.slice(marker.position);
|
||||
}
|
||||
return `// @Filename: ${fileName}\n${fileContent}`;
|
||||
}).join("\n\n");
|
||||
}
|
||||
|
||||
private verifyDefinitionTextSpan(defs: ts.DefinitionInfoAndBoundSpan, startMarkerName: string) {
|
||||
const range = this.testData.ranges.find(range => this.markerName(range.marker!) === startMarkerName);
|
||||
|
||||
@@ -839,6 +827,22 @@ export class TestState {
|
||||
}
|
||||
}
|
||||
|
||||
private renderMarkers(markers: { text: string, fileName: string, position: number }[], useTerminalBoldSequence = true) {
|
||||
const filesToDisplay = ts.deduplicate(markers.map(m => m.fileName), ts.equateValues);
|
||||
return filesToDisplay.map(fileName => {
|
||||
const markersToRender = markers.filter(m => m.fileName === fileName).sort((a, b) => b.position - a.position);
|
||||
let fileContent = this.tryGetFileContent(fileName) || "";
|
||||
for (const marker of markersToRender) {
|
||||
fileContent = fileContent.slice(0, marker.position) + bold(`/*${marker.text}*/`) + fileContent.slice(marker.position);
|
||||
}
|
||||
return `// @Filename: ${fileName}\n${fileContent}`;
|
||||
}).join("\n\n");
|
||||
|
||||
function bold(text: string) {
|
||||
return useTerminalBoldSequence ? `\x1b[1;4m${text}\x1b[0;31m` : text;
|
||||
}
|
||||
}
|
||||
|
||||
public verifyGetEmitOutputForCurrentFile(expected: string): void {
|
||||
const emit = this.languageService.getEmitOutput(this.activeFile.fileName);
|
||||
if (emit.outputFiles.length !== 1) {
|
||||
@@ -3188,12 +3192,53 @@ export class TestState {
|
||||
if (!negative && !validBraceCompletion) {
|
||||
this.raiseError(`${position} is not a valid brace completion position for ${openingBrace}`);
|
||||
}
|
||||
|
||||
if (negative && validBraceCompletion) {
|
||||
this.raiseError(`${position} is a valid brace completion position for ${openingBrace}`);
|
||||
}
|
||||
}
|
||||
|
||||
public baselineAutoImports(markerName: string, preferences?: ts.UserPreferences) {
|
||||
const marker = this.getMarkerByName(markerName);
|
||||
const baselineFile = this.getBaselineFileNameForContainingTestFile(`.baseline.md`);
|
||||
const completionPreferences = {
|
||||
includeCompletionsForModuleExports: true,
|
||||
includeCompletionsWithInsertText: true,
|
||||
allowIncompleteCompletions: true,
|
||||
includeCompletionsWithSnippetText: true,
|
||||
...preferences
|
||||
};
|
||||
|
||||
const ext = ts.getAnyExtensionFromPath(this.activeFile.fileName).slice(1);
|
||||
const lang = ["mts", "cts"].includes(ext) ? "ts" : ext;
|
||||
let baselineText = codeFence(this.renderMarkers([{ text: "|", fileName: marker.fileName, position: marker.position }], /*useTerminalBoldSequence*/ false), lang) + "\n\n";
|
||||
this.goToMarker(marker);
|
||||
|
||||
const completions = this.getCompletionListAtCaret(completionPreferences)!;
|
||||
|
||||
const autoImportCompletions = completions.entries.filter(c => c.hasAction && c.source && c.sortText === ts.Completions.SortText.AutoImportSuggestions);
|
||||
if (autoImportCompletions.length) {
|
||||
baselineText += `## From completions\n\n${autoImportCompletions.map(c => `- \`${c.name}\` from \`"${c.source}"\``).join("\n")}\n\n`;
|
||||
autoImportCompletions.forEach(c => {
|
||||
const details = this.getCompletionEntryDetails(c.name, c.source, c.data, completionPreferences);
|
||||
assert(details?.codeActions, `Entry '${c.name}' from "${c.source}" returned no code actions from completion details request`);
|
||||
assert(details.codeActions.length === 1, `Entry '${c.name}' from "${c.source}" returned more than one code action`);
|
||||
assert(details.codeActions[0].changes.length === 1, `Entry '${c.name}' from "${c.source}" returned a code action changing more than one file`);
|
||||
assert(details.codeActions[0].changes[0].fileName === this.activeFile.fileName, `Entry '${c.name}' from "${c.source}" returned a code action changing a different file`);
|
||||
const changes = details.codeActions[0].changes[0].textChanges;
|
||||
const completionChange: ts.TextChange = { newText: c.insertText || c.name, span: c.replacementSpan || completions.optionalReplacementSpan || { start: marker.position, length: 0 } };
|
||||
const sortedChanges = [...changes, completionChange].sort((a, b) => a.span.start - b.span.start);
|
||||
let newFileContent = this.activeFile.content;
|
||||
for (let i = sortedChanges.length - 1; i >= 0; i--) {
|
||||
newFileContent = newFileContent.substring(0, sortedChanges[i].span.start) + sortedChanges[i].newText + newFileContent.substring(sortedChanges[i].span.start + sortedChanges[i].span.length);
|
||||
}
|
||||
baselineText += codeFence(newFileContent, lang) + "\n\n";
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: do codefixes too
|
||||
Harness.Baseline.runBaseline(baselineFile, baselineText);
|
||||
}
|
||||
|
||||
public verifyJsxClosingTag(map: { [markerName: string]: ts.JsxClosingTagInfo | undefined }): void {
|
||||
for (const markerName in map) {
|
||||
this.goToMarker(markerName);
|
||||
@@ -4605,10 +4650,10 @@ function rangesOfDiffBetweenTwoStrings(source: string, target: string) {
|
||||
else {
|
||||
ranges.push({ start: index - 1, length: 1 });
|
||||
}
|
||||
}
|
||||
else {
|
||||
}
|
||||
else {
|
||||
ranges.push({ start: index - 1, length: 1 });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (let index = 0; index < Math.max(source.length, target.length); index++) {
|
||||
@@ -4618,10 +4663,10 @@ function rangesOfDiffBetweenTwoStrings(source: string, target: string) {
|
||||
}
|
||||
|
||||
return ranges;
|
||||
}
|
||||
}
|
||||
|
||||
// Adds an _ when the source string and the target string have a whitespace difference
|
||||
function highlightDifferenceBetweenStrings(source: string, target: string) {
|
||||
// Adds an _ when the source string and the target string have a whitespace difference
|
||||
function highlightDifferenceBetweenStrings(source: string, target: string) {
|
||||
const ranges = rangesOfDiffBetweenTwoStrings(source, target);
|
||||
let emTarget = target;
|
||||
ranges.forEach((range, index) => {
|
||||
@@ -4633,8 +4678,12 @@ function rangesOfDiffBetweenTwoStrings(source: string, target: string) {
|
||||
range.start + 1 + additionalOffset,
|
||||
range.start + range.length + 1 + additionalOffset
|
||||
);
|
||||
const after = emTarget.slice(range.start + range.length + 1 + additionalOffset, emTarget.length);
|
||||
const after = emTarget.slice(range.start + range.length + 1 + additionalOffset, emTarget.length);
|
||||
emTarget = before + lhs + between + rhs + after;
|
||||
});
|
||||
return emTarget;
|
||||
}
|
||||
}
|
||||
|
||||
function codeFence(code: string, lang?: string) {
|
||||
return `\`\`\`${lang || ""}\n${code}\n\`\`\``;
|
||||
}
|
||||
|
||||
@@ -488,6 +488,10 @@ export class Verify extends VerifyNegatable {
|
||||
this.state.verifyImportFixModuleSpecifiers(marker, moduleSpecifiers, preferences);
|
||||
}
|
||||
|
||||
public baselineAutoImports(marker: string, preferences?: ts.UserPreferences) {
|
||||
this.state.baselineAutoImports(marker, preferences);
|
||||
}
|
||||
|
||||
public navigationBar(json: any, options?: { checkSpans?: boolean }) {
|
||||
this.state.verifyNavigationBar(json, options);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user