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']],
|
||||
]);
|
||||
export function resolveImportSpecifierExtension(resolved: string): string | undefined {
|
||||
if (fs.existsSync(resolved))
|
||||
if (fileExists(resolved))
|
||||
return resolved;
|
||||
|
||||
for (const [ext, others] of kExtLookups) {
|
||||
if (!resolved.endsWith(ext))
|
||||
continue;
|
||||
for (const other of others) {
|
||||
const modified = resolved.substring(0, resolved.length - ext.length) + other;
|
||||
if (fs.existsSync(modified))
|
||||
if (fileExists(modified))
|
||||
return modified;
|
||||
}
|
||||
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/);
|
||||
});
|
||||
|
||||
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 }) => {
|
||||
const result = await runInlineTest({
|
||||
'package.json': `{ "type": "module" }`,
|
||||
|
||||
@ -594,6 +594,126 @@ test('should remove type imports from ts', async ({ runInlineTest }) => {
|
||||
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 }) => {
|
||||
const result = await runInlineTest({
|
||||
'a.test.ts': `
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user