Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ jobs:
node-version-file: ".nvmrc"
- run: npm ci
- run: node --run lint
- run: node --run format
- run: node --run type-check

validate-yaml:
Expand Down
6 changes: 2 additions & 4 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
{
"recommendations": [
"biomejs.biome"
]
}
"recommendations": ["biomejs.biome"]
}
20 changes: 10 additions & 10 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"editor.formatOnSave": true,
"javascript.updateImportsOnFileMove.enabled": "always",
"typescript.updateImportsOnFileMove.enabled": "always",
"editor.formatOnPaste": true,
"editor.wordWrap": "wordWrapColumn",
"editor.wordWrapColumn": 100,
"[markdown]": {
"editor.wordWrap": "off"
},
"typescript.experimental.useTsgo": true
"editor.formatOnSave": true,
"javascript.updateImportsOnFileMove.enabled": "always",
"typescript.updateImportsOnFileMove.enabled": "always",
"editor.formatOnPaste": true,
"editor.wordWrap": "wordWrapColumn",
"editor.wordWrapColumn": 100,
"[markdown]": {
"editor.wordWrap": "off"
},
"typescript.experimental.useTsgo": true
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
"scripts": {
"lint:fix": "biome lint --fix ./",
"lint": "biome lint ./",
"pre-commit": "node --run lint:fix; node --run type-check; node --run test",
"format": "biome format ./",
"format:fix": "biome format --fix ./",
"pre-commit": "node --run lint:fix; node --run format:fix; node --run type-check; node --run test",
"test": "npm run test --workspaces --if-present",
"test-legacy": "npm run test-legacy --workspaces --if-present",
"type-check": "tsgo --noEmit"
Expand Down
7 changes: 1 addition & 6 deletions recipes/correct-ts-specifiers/.codemodrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@
"arguments": [],
"meta": {
"git": "https://github.com/nodejs/userland-migrations/tree/main/recipes/correct-ts-specifiers",
"tags": [
"esm",
"import",
"node",
"typescript"
]
"tags": ["esm", "import", "node", "typescript"]
}
}
4 changes: 3 additions & 1 deletion recipes/correct-ts-specifiers/src/exts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ export const suspectExts = {
export type SuspectExt = keyof typeof suspectExts;
export type JSExt = keyof typeof jsToTSExts;
export const jsExts = Object.keys(jsToTSExts) as ReadonlyArray<JSExt>;
export const tsExts = Object.values(jsToTSExts) as ReadonlyArray<typeof jsToTSExts[JSExt]>;
export const tsExts = Object.values(jsToTSExts) as ReadonlyArray<
(typeof jsToTSExts)[JSExt]
>;
export type TSExt = (typeof tsExts)[number];

/**
Expand Down
256 changes: 149 additions & 107 deletions recipes/correct-ts-specifiers/src/fexists.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,144 +6,186 @@ import type { FSAbsolutePath } from './index.d.ts';

type FSAccess = typeof import('node:fs/promises').access;
type FExists = typeof import('./fexists.ts').fexists;
type ResolveSpecifier = typeof import('./resolve-specifier.ts').resolveSpecifier;
type ResolveSpecifier =
typeof import('./resolve-specifier.ts').resolveSpecifier;

const RESOLVED_SPECIFIER_ERR = 'Resolved specifier did not match expected';

describe('fexists', { concurrency: false /* concurrency clobbers `before`s */ }, () => {
const parentPath = '/tmp/test.ts';
const constants = { F_OK: null };

let mock__access: Mock<FSAccess>['mock'];
let mock__resolveSpecifier: Mock<ResolveSpecifier>['mock'];

before(() => {
const access = mock.fn<FSAccess>();
({ mock: mock__access } = access);
mock.module('node:fs/promises', {
namedExports: {
access,
constants,
},
});
describe(
'fexists',
{ concurrency: false /* concurrency clobbers `before`s */ },
() => {
const parentPath = '/tmp/test.ts';
const constants = { F_OK: null };

let mock__access: Mock<FSAccess>['mock'];
let mock__resolveSpecifier: Mock<ResolveSpecifier>['mock'];

before(() => {
const access = mock.fn<FSAccess>();
({ mock: mock__access } = access);
mock.module('node:fs/promises', {
namedExports: {
access,
constants,
},
});

const resolveSpecifier = mock.fn<ResolveSpecifier>();
({ mock: mock__resolveSpecifier } = resolveSpecifier);
mock.module('./resolve-specifier.ts', {
namedExports: {
resolveSpecifier,
},
});
mock__resolveSpecifier.mockImplementation(function MOCK__resolveSpecifier(_pp, specifier) {
return specifier;
const resolveSpecifier = mock.fn<ResolveSpecifier>();
({ mock: mock__resolveSpecifier } = resolveSpecifier);
mock.module('./resolve-specifier.ts', {
namedExports: {
resolveSpecifier,
},
});
mock__resolveSpecifier.mockImplementation(
function MOCK__resolveSpecifier(_pp, specifier) {
return specifier;
},
);
});
});

describe('when the file exists', () => {
let fexists: FExists;
describe('when the file exists', () => {
let fexists: FExists;

before(async () => {
mock__access.mockImplementation(async function MOCK_access() { });
before(async () => {
mock__access.mockImplementation(async function MOCK_access() {});

({ fexists } = await import('./fexists.ts'));
});
({ fexists } = await import('./fexists.ts'));
});

afterEach(() => {
mock__access.resetCalls();
});
afterEach(() => {
mock__access.resetCalls();
});

it('should return `true` for a bare specifier', async () => {
const specifier = 'foo';
const parentUrl = fileURLToPath(
import.meta.resolve('./fixtures/e2e/test.js'),
) as FSAbsolutePath;
it('should return `true` for a bare specifier', async () => {
const specifier = 'foo';
const parentUrl = fileURLToPath(
import.meta.resolve('./fixtures/e2e/test.js'),
) as FSAbsolutePath;

assert.equal(await fexists(parentUrl, specifier), true);
assert.equal(
mock__access.calls[0].arguments[0],
specifier,
RESOLVED_SPECIFIER_ERR,
);
});

assert.equal(await fexists(parentUrl, specifier), true);
assert.equal(mock__access.calls[0].arguments[0], specifier, RESOLVED_SPECIFIER_ERR);
});
it('should return `true` for a relative specifier', async () => {
const specifier = 'exists.js';

it('should return `true` for a relative specifier', async () => {
const specifier = 'exists.js';
assert.equal(await fexists(parentPath, specifier), true);
assert.equal(
mock__access.calls[0].arguments[0],
specifier,
RESOLVED_SPECIFIER_ERR,
);
});

assert.equal(await fexists(parentPath, specifier), true);
assert.equal(mock__access.calls[0].arguments[0], specifier, RESOLVED_SPECIFIER_ERR);
});
it('should return `true` for specifier with a query parameter', async () => {
const specifier = 'exists.js?v=1';

it('should return `true` for specifier with a query parameter', async () => {
const specifier = 'exists.js?v=1';
assert.equal(await fexists(parentPath, specifier), true);
assert.equal(
mock__access.calls[0].arguments[0],
specifier,
RESOLVED_SPECIFIER_ERR,
);
});

assert.equal(await fexists(parentPath, specifier), true);
assert.equal(mock__access.calls[0].arguments[0], specifier, RESOLVED_SPECIFIER_ERR);
});
it('should return `true` for an absolute specifier', async () => {
assert.equal(await fexists(parentPath, '/tmp/foo/exists.js'), true);
assert.equal(
mock__access.calls[0].arguments[0],
'/tmp/foo/exists.js',
RESOLVED_SPECIFIER_ERR,
);
});

it('should return `true` for an absolute specifier', async () => {
assert.equal(await fexists(parentPath, '/tmp/foo/exists.js'), true);
assert.equal(
mock__access.calls[0].arguments[0],
'/tmp/foo/exists.js',
RESOLVED_SPECIFIER_ERR,
);
it('should return `true` for a URL', async () => {
assert.equal(
await fexists(parentPath, 'file://localhost/foo/exists.js'),
true,
);
assert.equal(
mock__access.calls[0].arguments[0],
'file://localhost/foo/exists.js',
RESOLVED_SPECIFIER_ERR,
);
});
});

it('should return `true` for a URL', async () => {
assert.equal(await fexists(parentPath, 'file://localhost/foo/exists.js'), true);
assert.equal(
mock__access.calls[0].arguments[0],
'file://localhost/foo/exists.js',
RESOLVED_SPECIFIER_ERR,
);
});
});
describe('when the file does NOT exists', () => {
let fexists: FExists;

describe('when the file does NOT exists', () => {
let fexists: FExists;
before(async () => {
mock__access.mockImplementation(async function MOCK_access() {
throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' });
});

before(async () => {
mock__access.mockImplementation(async function MOCK_access() {
throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' });
({ fexists } = await import('./fexists.ts'));
});

({ fexists } = await import('./fexists.ts'));
});
afterEach(() => {
mock__access.resetCalls();
});

afterEach(() => {
mock__access.resetCalls();
});
it('should return `false` for a relative specifier', async () => {
const specifier = 'noexists.js';

it('should return `false` for a relative specifier', async () => {
const specifier = 'noexists.js';
assert.equal(await fexists(parentPath, specifier), false);
assert.equal(
mock__access.calls[0].arguments[0],
specifier,
RESOLVED_SPECIFIER_ERR,
);
});

assert.equal(await fexists(parentPath, specifier), false);
assert.equal(mock__access.calls[0].arguments[0], specifier, RESOLVED_SPECIFIER_ERR);
});
it('should return `false` for a relative specifier', async () => {
const specifier = 'noexists.js?v=1';

it('should return `false` for a relative specifier', async () => {
const specifier = 'noexists.js?v=1';
assert.equal(await fexists(parentPath, specifier), false);
assert.equal(
mock__access.calls[0].arguments[0],
specifier,
RESOLVED_SPECIFIER_ERR,
);
});

assert.equal(await fexists(parentPath, specifier), false);
assert.equal(mock__access.calls[0].arguments[0], specifier, RESOLVED_SPECIFIER_ERR);
});
it('should return `false` for an absolute specifier', async () => {
const specifier = '/tmp/foo/noexists.js';

it('should return `false` for an absolute specifier', async () => {
const specifier = '/tmp/foo/noexists.js';
assert.equal(await fexists(parentPath, specifier), false);
assert.equal(
mock__access.calls[0].arguments[0],
specifier,
RESOLVED_SPECIFIER_ERR,
);
});

assert.equal(await fexists(parentPath, specifier), false);
assert.equal(mock__access.calls[0].arguments[0], specifier, RESOLVED_SPECIFIER_ERR);
});
it('should return `false` for a URL specifier', async () => {
const specifier = 'file://localhost/foo/noexists.js';

it('should return `false` for a URL specifier', async () => {
const specifier = 'file://localhost/foo/noexists.js';
assert.equal(await fexists(parentPath, specifier), false);
assert.equal(
mock__access.calls[0].arguments[0],
specifier,
RESOLVED_SPECIFIER_ERR,
);
});

assert.equal(await fexists(parentPath, specifier), false);
assert.equal(mock__access.calls[0].arguments[0], specifier, RESOLVED_SPECIFIER_ERR);
});
it('should return `false` when the specifier can’t be resolved', async () => {
mock__resolveSpecifier.mockImplementationOnce(
function MOCK__resolveSpecifier(_pp, _specifier) {
throw Object.assign(new Error('ERR_MODULE_NOT_FOUND'), {
code: 'ERR_MODULE_NOT_FOUND',
});
},
);

it('should return `false` when the specifier can’t be resolved', async () => {
mock__resolveSpecifier.mockImplementationOnce(function MOCK__resolveSpecifier(_pp, _specifier) {
throw Object.assign(new Error('ERR_MODULE_NOT_FOUND'), { code: 'ERR_MODULE_NOT_FOUND' });
assert.equal(await fexists(parentPath, 'noexists'), false);
});

assert.equal(await fexists(parentPath, 'noexists'), false);
});
});
});
},
);
8 changes: 6 additions & 2 deletions recipes/correct-ts-specifiers/src/fexists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ export function fexists(
) {
let resolvedSpecifier: FSAbsolutePath;
try {
resolvedSpecifier = resolveSpecifier(parentPath, specifier) as FSAbsolutePath;
resolvedSpecifier = resolveSpecifier(
parentPath,
specifier,
) as FSAbsolutePath;
} catch (err) {
if ((err as NodeJS.ErrnoException).code !== 'ERR_MODULE_NOT_FOUND') throw err;
if ((err as NodeJS.ErrnoException).code !== 'ERR_MODULE_NOT_FOUND')
throw err;
return false;
}

Expand Down
8 changes: 5 additions & 3 deletions recipes/correct-ts-specifiers/src/get-not-found-url.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ describe('Get module not found url', { concurrency: true }, () => {
const errUrl = `file://${errPath}`;
function makeError(setUrlProp = false) {
const err = new Error(
['[ERR_MODULE_NOT_FOUND]: Cannot find module', `'${errPath}'`, 'imported from /tmp/bar'].join(
' ',
),
[
'[ERR_MODULE_NOT_FOUND]: Cannot find module',
`'${errPath}'`,
'imported from /tmp/bar',
].join(' '),
);

if (setUrlProp) Object.assign(err, { url: errPath });
Expand Down
Loading
Loading