Skip to content

Commit

Permalink
fix(typescript): allow directory imports
Browse files Browse the repository at this point in the history
This updates previous work in microsoft#22887 to align more fully with `--moduleResolution=bundler`, allowing index files to be imported with the /index extension
  • Loading branch information
kristojorg committed May 24, 2023
1 parent 76b4b9d commit 6648b22
Show file tree
Hide file tree
Showing 3 changed files with 268 additions and 2 deletions.
18 changes: 16 additions & 2 deletions packages/playwright-test/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = `${resolved}/index`
return resolveImportSpecifierExtension(dirImport)
}
}

function fileExists(resolved: string) {
return fs.existsSync(resolved) && !fs.lstatSync(resolved).isDirectory()
}

function dirExists(resolved: string) {
return fs.existsSync(resolved) && fs.lstatSync(resolved).isDirectory()
}
132 changes: 132 additions & 0 deletions tests/playwright-test/esm.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.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 ESM mode', async ({ runInlineTest }) => {
const result = await runInlineTest({
'package.json': `{ "type": "module" }`,
Expand Down
120 changes: 120 additions & 0 deletions tests/playwright-test/loader.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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': `
Expand Down

0 comments on commit 6648b22

Please sign in to comment.