Skip to content

Commit

Permalink
[New] add types
Browse files Browse the repository at this point in the history
  • Loading branch information
ljharb committed Sep 19, 2024
1 parent abd2974 commit 3caf579
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 26 deletions.
71 changes: 47 additions & 24 deletions api.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,37 @@ const {
multi,
'ignore-dirs': rawIgnoreDirs,
},
} = pargs(help, import.meta.filename, {
allowPositionals: true,
options: {
bound: { type: 'boolean' },
property: { type: 'boolean' },
'skip-shim-returns-polyfill': { type: 'boolean' },
'skip-auto-shim': { type: 'boolean' },
multi: { type: 'boolean' },
'ignore-dirs': {
type: 'string',
multiple: true,
default: [],
// eslint-disable-next-line no-extra-parens, max-len
} = /** @type {{ positionals: string[], values: { bound: boolean, property: boolean, 'skip-shim-returns-polyfill': boolean, 'skip-auto-shim': boolean, 'ignore-dirs': string[], multi: boolean } }} */ (
pargs(help, import.meta.filename, {
allowPositionals: true,
options: {
bound: { type: 'boolean' },
property: { type: 'boolean' },
'skip-shim-returns-polyfill': { type: 'boolean' },
'skip-auto-shim': { type: 'boolean' },
multi: { type: 'boolean' },
'ignore-dirs': {
type: 'string',
multiple: true,
default: [],
},
},
},
});
})
);

let isMulti = multi;

const ignoreDirs = ['node_modules', 'coverage', 'helpers', 'test', 'aos'].concat(rawIgnoreDirs.flatMap((x) => x.split(',')));

/** @type {<T extends string>(name: T) => [T, T]} */
function makeEntries(name) {
return [name, name];
}
/** @type {[string, string | [string, string]][]} */
const moduleNames = positionals.map(makeEntries);

/** @type {{ name?: string, main?: string | false, exports?: Record<string, unknown | unknown[]> }} */
let pkg;
if (moduleNames.length < 1) {
const packagePath = path.join(process.cwd(), 'package.json');
Expand All @@ -83,13 +90,17 @@ if (moduleNames.length < 1) {
}
}

/** @type {(name: string) => {}} */
function requireOrEvalError(name) {
try {
return require(name);
} catch (e) {
// @ts-expect-error it's fine if this throws on a nullish exception from the module
return new EvalError(e.message);
}
}

/** @type {(t: test.Test, prefix: string, packageDir: string, asMulti: boolean) => void} */
const testAuto = function testAutoModule(t, prefix, packageDir, asMulti) {
t.test(`${prefix}auto`, (st) => {
const msg = 'auto is present';
Expand All @@ -107,14 +118,17 @@ const testAuto = function testAutoModule(t, prefix, packageDir, asMulti) {
}
});
};

/** @type {(t: test.Test, packageDir: string, name: string) => undefined | EvalError} */
const doValidation = function doActualValidation(t, packageDir, name) {
const module = requireOrEvalError(name);
if (module instanceof EvalError) {
return module;
}
const implementation = requireOrEvalError(`${packageDir}/implementation`);
const shim = requireOrEvalError(`${packageDir}/shim`);
const getPolyfill = requireOrEvalError(`${packageDir}/polyfill`);
// eslint-disable-next-line no-extra-parens
const getPolyfill = /** @type {Function} */ (requireOrEvalError(`${packageDir}/polyfill`));

const prefix = isMulti ? `${path.basename(packageDir)}: ` : '';

Expand Down Expand Up @@ -203,39 +217,44 @@ const doValidation = function doActualValidation(t, packageDir, name) {
return void undefined;
};

/** @type {(t: test.Test, nameOrFilePaths: string | [string, string]) => EvalError | undefined} */
const validateModule = function validateAPIModule(t, nameOrFilePaths) {
const [name, packageDir] = Array.isArray(nameOrFilePaths)
? nameOrFilePaths
: [nameOrFilePaths, nameOrFilePaths];

t.test('`exports` field', { skip: !('exports' in pkg) }, (st) => {
// eslint-disable-next-line no-extra-parens
const exps = /** @type {NonNullable<typeof pkg.exports>} */ (pkg.exports);
const expectedKeys = isMulti
? ['.', './auto', './shim', './package.json']
: ['.', './auto', './polyfill', './implementation', './shim', './package.json'];

const exportsKeys = Object.keys(pkg.exports);
const exportsKeys = Object.keys(exps);

const keysToCheck = exportsKeys.filter((key) => expectedKeys.includes(key));
st.deepEqual(keysToCheck, expectedKeys, 'expected entrypoints are present in the proper order');

exportsKeys.forEach((key) => {
const rhs = pkg.exports[key];
const rhs = exps[key];
// @ts-expect-error TS sucks with concat
const exists = [].concat(rhs).some(existsSync);
st.ok(exists, `entrypoint \`${key}\` points to \`${inspect(rhs)}\` which exists (or is an array with one item that exists)`);
});

st.equal(pkg.exports['./package.json'], './package.json', 'package.json is exposed');
st.equal(exps['./package.json'], './package.json', 'package.json is exposed');

st.end();
});

if (isMulti) {
const subPackages = requireOrEvalError(name);
// eslint-disable-next-line no-extra-parens
const subPackages = /** @type {string[]} */ (requireOrEvalError(name));
if (subPackages instanceof EvalError) {
return subPackages;
}
subPackages.sort();
t.ok(Array.isArray(subPackages), 'main export is an array of sub packages');
subPackages.sort();
t.deepEqual(
Object.keys(subPackages),
subPackages.map((_, i) => String(i)),
Expand All @@ -259,6 +278,8 @@ const validateModule = function validateAPIModule(t, nameOrFilePaths) {
});

t.test('subpackages, `exports` field', { skip: !('exports' in pkg) }, (st) => {
// eslint-disable-next-line no-extra-parens
const exps = /** @type {NonNullable<typeof pkg.exports>} */ (pkg.exports);
subPackages.forEach((subPackage) => {
const subPackageLHS = [
`./${subPackage}`,
Expand All @@ -269,9 +290,11 @@ const validateModule = function validateAPIModule(t, nameOrFilePaths) {
];

subPackageLHS.forEach((lhs) => {
st.ok(lhs in pkg.exports, `\`${lhs}\` is in \`exports\``);
if (lhs in pkg.exports) {
const rhs = pkg.exports[lhs];
st.ok(lhs in exps, `\`${lhs}\` is in \`exports\``);
if (lhs in exps) {
// eslint-disable-next-line no-extra-parens
const rhs = /** @type {string} */ (exps[lhs]);
st.equal(typeof rhs, 'string', 'right-hand side of `exports` is a string');
const resolved = path.resolve(path.join(packageDir, rhs));
const lhsGuess = `./${path.relative(
packageDir,
Expand All @@ -285,7 +308,7 @@ const validateModule = function validateAPIModule(t, nameOrFilePaths) {
});

st.deepEqual(
Object.keys(pkg.exports).filter((lhs) => subPackageLHS.indexOf(lhs) > -1),
Object.keys(exps).filter((lhs) => subPackageLHS.indexOf(lhs) > -1),
subPackageLHS,
`subpackage \`${subPackage}\` exports the expected entries in the proper order`,
);
Expand Down
2 changes: 2 additions & 0 deletions fakeShim.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
'use strict';

/** @type {[unknown, unknown[]][]} */
const calls = [];
/** @type {{ calls: typeof calls } & ((this: unknown) => void)} */
const shim = function fakeShim(...args) {
calls.push([this, args]);
};
Expand Down
Empty file added index.d.ts
Empty file.
1 change: 1 addition & 0 deletions multiAutoTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ assert.equal(args, 0, 'wrong number of arguments; expected 0');
const fakeShim = require('./fakeShim');
assert.equal(fakeShim.calls.length, 0, 'shims are not yet called');

/** @type {string[]} */
const subPackages = require(process.cwd());
subPackages.forEach((subPackage) => {
require.cache[require.resolve(path.join(process.cwd(), subPackage, 'shim'))] = require.cache[require.resolve('./fakeShim')];
Expand Down
14 changes: 12 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"exports": {
"./package.json": "./package.json"
},
"types": "index.d.ts",
"license": "MIT",
"scripts": {
"prepack": "npmignore --auto --commentLines=autogenerated",
Expand All @@ -24,7 +25,8 @@
"test": "npm run --silent tests-only",
"posttest": "npx npm@'>=10.2' audit --production",
"tests-only": "echo 'no tests yet :-('",
"lint": "eslint --ext=js,mjs ."
"lint": "eslint --ext=js,mjs .",
"postlint": "tsc && attw -P"
},
"repository": {
"type": "git",
Expand All @@ -50,12 +52,20 @@
"tape": "^5.8.1"
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.15.4",
"@ljharb/eslint-config": "^21.1.1",
"@ljharb/tsconfig": "^0.2.0",
"@types/node": "^20.16.5",
"@types/object-inspect": "^1.13.0",
"@types/object.groupby": "^1.0.4",
"@types/semver": "^7.5.8",
"@types/tape": "^5.6.4",
"auto-changelog": "^2.4.0",
"eslint": "=8.8.0",
"in-publish": "^2.0.1",
"npmignore": "^0.3.1",
"safe-publish-latest": "^2.0.0"
"safe-publish-latest": "^2.0.0",
"typescript": "next"
},
"testling": {
"files": "test.js"
Expand Down
13 changes: 13 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "@ljharb/tsconfig",
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "nodenext",
"resolveJsonModule": true,
"maxNodeModuleJsDepth": 1,
},
"exclude": [
"coverage",
],
}

0 comments on commit 3caf579

Please sign in to comment.