Skip to content

Commit cfdef9d

Browse files
TypeScript Bota-tarasyuk
TypeScript Bot
andauthored
Cherry-pick PR #47657 into release-4.6 (#48223)
Component commits: 4516fa8 fix(47597): ignore commented imports following template expression Co-authored-by: Oleksandr T <[email protected]>
1 parent e397ed1 commit cfdef9d

File tree

2 files changed

+207
-0
lines changed

2 files changed

+207
-0
lines changed

src/services/preProcess.ts

+36
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,42 @@ namespace ts {
345345
break;
346346
}
347347

348+
if (scanner.getToken() === SyntaxKind.TemplateHead) {
349+
const stack = [scanner.getToken()];
350+
let token = scanner.scan();
351+
loop: while (length(stack)) {
352+
switch (token) {
353+
case SyntaxKind.EndOfFileToken:
354+
break loop;
355+
case SyntaxKind.ImportKeyword:
356+
tryConsumeImport();
357+
break;
358+
case SyntaxKind.TemplateHead:
359+
stack.push(token);
360+
break;
361+
case SyntaxKind.OpenBraceToken:
362+
if (length(stack)) {
363+
stack.push(token);
364+
}
365+
break;
366+
case SyntaxKind.CloseBraceToken:
367+
if (length(stack)) {
368+
if (lastOrUndefined(stack) === SyntaxKind.TemplateHead) {
369+
if (scanner.reScanTemplateToken(/* isTaggedTemplate */ false) === SyntaxKind.TemplateTail) {
370+
stack.pop();
371+
}
372+
}
373+
else {
374+
stack.pop();
375+
}
376+
}
377+
break;
378+
}
379+
token = scanner.scan();
380+
}
381+
nextToken();
382+
}
383+
348384
// check if at least one of alternative have moved scanner forward
349385
if (tryConsumeDeclare() ||
350386
tryConsumeImport() ||

src/testRunner/unittests/services/preProcessFile.ts

+171
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,177 @@ describe("unittests:: services:: PreProcessFile:", () => {
176176
});
177177
});
178178

179+
it("Correctly ignore commented imports following template expression", () => {
180+
/* eslint-disable no-template-curly-in-string */
181+
test("/**" + "\n" +
182+
" * Before" + "\n" +
183+
" * ```" + "\n" +
184+
" * import * as a from \"a\";" + "\n" +
185+
" * ```" + "\n" +
186+
" */" + "\n" +
187+
"type Foo = `${string}`;" + "\n" +
188+
"/**" + "\n" +
189+
" * After" + "\n" +
190+
" * ```" + "\n" +
191+
" * import { B } from \"b\";" + "\n" +
192+
" * import * as c from \"c\";" + "\n" +
193+
" * ```" + "\n" +
194+
" */",
195+
/*readImportFile*/ true,
196+
/*detectJavaScriptImports*/ true,
197+
{
198+
referencedFiles: [],
199+
typeReferenceDirectives: [],
200+
libReferenceDirectives: [],
201+
importedFiles: [],
202+
ambientExternalModules: undefined,
203+
isLibFile: false
204+
});
205+
/* eslint-enable no-template-curly-in-string */
206+
});
207+
208+
it("Correctly returns imports after a template expression", () => {
209+
/* eslint-disable no-template-curly-in-string */
210+
test("`${foo}`; import \"./foo\";",
211+
/*readImportFile*/ true,
212+
/*detectJavaScriptImports*/ true,
213+
{
214+
referencedFiles: [],
215+
typeReferenceDirectives: [],
216+
libReferenceDirectives: [],
217+
importedFiles: [
218+
{ fileName: "./foo", pos: 17, end: 22 }
219+
],
220+
ambientExternalModules: undefined,
221+
isLibFile: false
222+
});
223+
/* eslint-enable no-template-curly-in-string */
224+
});
225+
226+
it("Correctly returns dynamic imports from template expression", () => {
227+
/* eslint-disable no-template-curly-in-string */
228+
test("`${(<div>Text `` ${} text {} " + "\n" +
229+
"${import(\"a\")} {import(\"b\")} " + "\n" +
230+
"${/* A comment */} ${/* import(\"ignored\") */} </div>)}`",
231+
/*readImportFile*/ true,
232+
/*detectJavaScriptImports*/ true,
233+
{
234+
referencedFiles: [],
235+
typeReferenceDirectives: [],
236+
libReferenceDirectives: [],
237+
importedFiles: [
238+
{ fileName: "a", pos: 39, end: 40 },
239+
{ fileName: "b", pos: 53, end: 54 }
240+
],
241+
ambientExternalModules: undefined,
242+
isLibFile: false
243+
});
244+
/* eslint-enable no-template-curly-in-string */
245+
});
246+
247+
it("Correctly returns dynamic imports from nested template expression", () => {
248+
/* eslint-disable no-template-curly-in-string */
249+
test("`${foo(`${bar(`${import(\"a\")} ${import(\"b\")}`, `${baz(`${import(\"c\") ${import(\"d\")}`)}`)}`)}`",
250+
/*readImportFile*/ true,
251+
/*detectJavaScriptImports*/ true,
252+
{
253+
referencedFiles: [],
254+
typeReferenceDirectives: [],
255+
libReferenceDirectives: [],
256+
importedFiles: [
257+
{ fileName: "a", pos: 24, end: 25 },
258+
{ fileName: "b", pos: 39, end: 40 },
259+
{ fileName: "c", pos: 64, end: 65 },
260+
{ fileName: "d", pos: 78, end: 79 },
261+
],
262+
ambientExternalModules: undefined,
263+
isLibFile: false
264+
});
265+
/* eslint-enable no-template-curly-in-string */
266+
});
267+
268+
it("Correctly returns dynamic imports from tagged template expression", () => {
269+
/* eslint-disable no-template-curly-in-string */
270+
test("foo`${ fn({ a: 100 }, import(\"a\"), `${import(\"b\")}`, import(\"c\"), `${import(\"d\")} foo`, import(\"e\")) }`",
271+
/*readImportFile*/ true,
272+
/*detectJavaScriptImports*/ true,
273+
{
274+
referencedFiles: [],
275+
typeReferenceDirectives: [],
276+
libReferenceDirectives: [],
277+
importedFiles: [
278+
{ fileName: "a", pos: 29, end: 30 },
279+
{ fileName: "b", pos: 45, end: 46 },
280+
{ fileName: "c", pos: 60, end: 61 },
281+
{ fileName: "d", pos: 76, end: 77 },
282+
{ fileName: "e", pos: 95, end: 96 },
283+
],
284+
ambientExternalModules: undefined,
285+
isLibFile: false
286+
});
287+
/* eslint-enable no-template-curly-in-string */
288+
});
289+
290+
it("Correctly returns dynamic imports from template expression and imports following it", () => {
291+
/* eslint-disable no-template-curly-in-string */
292+
test("const x = `hello ${await import(\"a\").default}`;" + "\n\n" +
293+
"import { y } from \"b\";",
294+
/*readImportFile*/ true,
295+
/*detectJavaScriptImports*/ true,
296+
{
297+
referencedFiles: [],
298+
typeReferenceDirectives: [],
299+
libReferenceDirectives: [],
300+
importedFiles: [
301+
{ fileName: "a", pos: 32, end: 33 },
302+
{ fileName: "b", pos: 67, end: 68 },
303+
],
304+
ambientExternalModules: undefined,
305+
isLibFile: false
306+
});
307+
/* eslint-enable no-template-curly-in-string */
308+
});
309+
310+
it("Correctly returns dynamic imports from template expressions and other imports", () => {
311+
/* eslint-disable no-template-curly-in-string */
312+
test("const x = `x ${await import(\"a\").default}`;" + "\n\n" +
313+
"import { y } from \"b\";" + "\n" +
314+
"const y = `y ${import(\"c\")}`;" + "\n\n" +
315+
"import { d } from \"d\";",
316+
/*readImportFile*/ true,
317+
/*detectJavaScriptImports*/ true,
318+
{
319+
referencedFiles: [],
320+
typeReferenceDirectives: [],
321+
libReferenceDirectives: [],
322+
importedFiles: [
323+
{ fileName: "a", pos: 28, end: 29 },
324+
{ fileName: "b", pos: 63, end: 64 },
325+
{ fileName: "c", pos: 90, end: 91 },
326+
{ fileName: "d", pos: 117, end: 118 },
327+
],
328+
ambientExternalModules: undefined,
329+
isLibFile: false
330+
});
331+
/* eslint-enable no-template-curly-in-string */
332+
});
333+
334+
it("Correctly returns empty importedFiles with incorrect template expression", () => {
335+
/* eslint-disable no-template-curly-in-string */
336+
test("const foo = `${",
337+
/*readImportFile*/ true,
338+
/*detectJavaScriptImports*/ true,
339+
{
340+
referencedFiles: [],
341+
typeReferenceDirectives: [],
342+
libReferenceDirectives: [],
343+
importedFiles: [],
344+
ambientExternalModules: undefined,
345+
isLibFile: false
346+
});
347+
/* eslint-enable no-template-curly-in-string */
348+
});
349+
179350
it("Correctly return ES6 exports", () => {
180351
test("export * from \"m1\";" + "\n" +
181352
"export {a} from \"m2\";" + "\n" +

0 commit comments

Comments
 (0)