mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix(typescript): allow directory imports (#23254)
This updates previous work in #22887 to align more fully with `--moduleResolution=bundler`, allowing index files to be imported with the /index extension --------- Signed-off-by: Kristo Jorgenson <kristojorg@users.noreply.github.com> Co-authored-by: Dmitry Gozman <dgozman@gmail.com>
This commit is contained in:
parent
24ac25212b
commit
d5d155df1f
@ -321,16 +321,30 @@ const kExtLookups = new Map([
|
|||||||
['', ['.js', '.ts', '.jsx', '.tsx', '.cjs', '.mjs', '.cts', '.mts']],
|
['', ['.js', '.ts', '.jsx', '.tsx', '.cjs', '.mjs', '.cts', '.mts']],
|
||||||
]);
|
]);
|
||||||
export function resolveImportSpecifierExtension(resolved: string): string | undefined {
|
export function resolveImportSpecifierExtension(resolved: string): string | undefined {
|
||||||
if (fs.existsSync(resolved))
|
if (fileExists(resolved))
|
||||||
return resolved;
|
return resolved;
|
||||||
|
|
||||||
for (const [ext, others] of kExtLookups) {
|
for (const [ext, others] of kExtLookups) {
|
||||||
if (!resolved.endsWith(ext))
|
if (!resolved.endsWith(ext))
|
||||||
continue;
|
continue;
|
||||||
for (const other of others) {
|
for (const other of others) {
|
||||||
const modified = resolved.substring(0, resolved.length - ext.length) + other;
|
const modified = resolved.substring(0, resolved.length - ext.length) + other;
|
||||||
if (fs.existsSync(modified))
|
if (fileExists(modified))
|
||||||
return modified;
|
return modified;
|
||||||
}
|
}
|
||||||
break; // Do not try '' when a more specific extesion like '.jsx' matched.
|
break; // Do not try '' when a more specific extesion like '.jsx' matched.
|
||||||
}
|
}
|
||||||
|
// try directory imports last
|
||||||
|
if (dirExists(resolved)) {
|
||||||
|
const dirImport = path.join(resolved, 'index');
|
||||||
|
return resolveImportSpecifierExtension(dirImport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileExists(resolved: string) {
|
||||||
|
return fs.statSync(resolved, { throwIfNoEntry: false })?.isFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
function dirExists(resolved: string) {
|
||||||
|
return fs.statSync(resolved, { throwIfNoEntry: false })?.isDirectory();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -220,6 +220,138 @@ test('should filter by line', async ({ runInlineTest }) => {
|
|||||||
expect(result.output).toMatch(/x\.spec\.ts.*two/);
|
expect(result.output).toMatch(/x\.spec\.ts.*two/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should resolve directory import to index.js file in ESM mode', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'package.json': `{ "type": "module" }`,
|
||||||
|
'playwright.config.ts': `export default { projects: [{name: 'foo'}] };`,
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { gimmeAOne } from './playwright-utils';
|
||||||
|
test('pass', ({}) => {
|
||||||
|
expect(gimmeAOne()).toBe(1);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'playwright-utils/index.js': `
|
||||||
|
export function gimmeAOne() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should resolve directory import to index.ts file in ESM mode', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'package.json': `{ "type": "module" }`,
|
||||||
|
'playwright.config.ts': `export default { projects: [{name: 'foo'}] };`,
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { gimmeAOne } from './playwright-utils';
|
||||||
|
test('pass', ({}) => {
|
||||||
|
expect(gimmeAOne()).toBe(1);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'playwright-utils/index.ts': `
|
||||||
|
export function gimmeAOne() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should resolve directory import to index.tsx file in ESM mode', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'package.json': `{ "type": "module" }`,
|
||||||
|
'playwright.config.ts': `export default { projects: [{name: 'foo'}] };`,
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { gimmeAOne } from './playwright-utils';
|
||||||
|
test('pass', ({}) => {
|
||||||
|
expect(gimmeAOne()).toBe(1);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'playwright-utils/index.tsx': `
|
||||||
|
export function gimmeAOne() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should resolve directory import to index.mjs file in ESM mode', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'package.json': `{ "type": "module" }`,
|
||||||
|
'playwright.config.ts': `export default { projects: [{name: 'foo'}] };`,
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { gimmeAOne } from './playwright-utils';
|
||||||
|
test('pass', ({}) => {
|
||||||
|
expect(gimmeAOne()).toBe(1);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'playwright-utils/index.mjs': `
|
||||||
|
export function gimmeAOne() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should resolve directory import to index.jsx file in ESM mode', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'package.json': `{ "type": "module" }`,
|
||||||
|
'playwright.config.ts': `export default { projects: [{name: 'foo'}] };`,
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { gimmeAOne } from './playwright-utils';
|
||||||
|
test('pass', ({}) => {
|
||||||
|
expect(gimmeAOne()).toBe(1);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'playwright-utils/index.jsx': `
|
||||||
|
export function gimmeAOne() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should resolve file import before directory import in ESM mode', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'package.json': `{ "type": "module" }`,
|
||||||
|
'playwright.config.ts': `export default { projects: [{name: 'foo'}] };`,
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { gimmeAOne } from './playwright-utils';
|
||||||
|
test('pass', ({}) => {
|
||||||
|
expect(gimmeAOne()).toBe(1);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'playwright-utils.js': `
|
||||||
|
export function gimmeAOne() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
'playwright-utils/index.js': `
|
||||||
|
export function gimmeAOne() {
|
||||||
|
// intentionally return the wrong thing because this file shouldn't be resolved.
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
test('should resolve .js import to .ts file in ESM mode', async ({ runInlineTest }) => {
|
test('should resolve .js import to .ts file in ESM mode', async ({ runInlineTest }) => {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'package.json': `{ "type": "module" }`,
|
'package.json': `{ "type": "module" }`,
|
||||||
|
|||||||
@ -594,6 +594,126 @@ test('should remove type imports from ts', async ({ runInlineTest }) => {
|
|||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should resolve directory import to index.js file in non-ESM mode', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { gimmeAOne } from './playwright-utils';
|
||||||
|
test('pass', ({}) => {
|
||||||
|
expect(gimmeAOne()).toBe(1);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'playwright-utils/index.js': `
|
||||||
|
export function gimmeAOne() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should resolve directory import to index.ts file in non-ESM mode', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { gimmeAOne } from './playwright-utils';
|
||||||
|
test('pass', ({}) => {
|
||||||
|
expect(gimmeAOne()).toBe(1);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'playwright-utils/index.ts': `
|
||||||
|
export function gimmeAOne() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should resolve directory import to index.tsx file in non-ESM mode', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { gimmeAOne } from './playwright-utils';
|
||||||
|
test('pass', ({}) => {
|
||||||
|
expect(gimmeAOne()).toBe(1);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'playwright-utils/index.tsx': `
|
||||||
|
export function gimmeAOne() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should resolve directory import to index.mjs file in non-ESM mode', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { gimmeAOne } from './playwright-utils';
|
||||||
|
test('pass', ({}) => {
|
||||||
|
expect(gimmeAOne()).toBe(1);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'playwright-utils/index.mjs': `
|
||||||
|
export function gimmeAOne() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should resolve directory import to index.jsx file in non-ESM mode', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { gimmeAOne } from './playwright-utils';
|
||||||
|
test('pass', ({}) => {
|
||||||
|
expect(gimmeAOne()).toBe(1);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'playwright-utils/index.jsx': `
|
||||||
|
export function gimmeAOne() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should resolve file import before directory import in non-ESM mode', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { gimmeAOne } from './playwright-utils';
|
||||||
|
test('pass', ({}) => {
|
||||||
|
expect(gimmeAOne()).toBe(1);
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
'playwright-utils.jsx': `
|
||||||
|
export function gimmeAOne() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
'playwright-utils/index.jsx': `
|
||||||
|
export function gimmeAOne() {
|
||||||
|
// intentionally return the wrong thing because this file shouldn't be resolved.
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
expect(result.passed).toBe(1);
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
test('should resolve .js import to .ts file in non-ESM mode', async ({ runInlineTest }) => {
|
test('should resolve .js import to .ts file in non-ESM mode', async ({ runInlineTest }) => {
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'a.test.ts': `
|
'a.test.ts': `
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user