Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add INVALID_REPOSITORY_VALUE rule #106

Merged
merged 20 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions pkg/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ export type Message =
}
>
| BaseMessage<'DEPRECATED_FIELD_JSNEXT'>
| BaseMessage<'INVALID_REPOSITORY_VALUE', {
directory?: string;
}>;

export interface Options {
/**
Expand Down
61 changes: 60 additions & 1 deletion pkg/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import {
getPkgPathValue,
replaceLast,
isRelativePath,
isAbsolutePath
isAbsolutePath,
isRepositoryUrl
} from './utils.js'

/**
Expand Down Expand Up @@ -234,6 +235,12 @@ export async function publint({ pkgDir, vfs, level, strict, _packedFiles }) {
}
}

// if `repository` field exist, check if the value is valid
// `repository` might be a shorthand string of URL or an object
if ('repository' in rootPkg) {
promiseQueue.push(() => checkRepositoryField(rootPkg.repository));
}

// check file existence for other known package fields
const knownFields = [
'types',
Expand Down Expand Up @@ -436,6 +443,58 @@ export async function publint({ pkgDir, vfs, level, strict, _packedFiles }) {
}
}

/**
* @param {Record<string, string> | string} repositoryField
*/
async function checkRepositoryField(repositoryField) {
if (typeof repositoryField === 'string') {
if (!isRepositoryUrl(repositoryField)) {
Namchee marked this conversation as resolved.
Show resolved Hide resolved
messages.push({
code: 'INVALID_REPOSITORY_VALUE',
args: {},
path: ['repository'],
type: 'warning',
});
}

return;
} else if (typeof repositoryField === 'object') {
if (repositoryField.url && !isRepositoryUrl(repositoryField.url)) {
messages.push({
code: 'INVALID_REPOSITORY_VALUE',
args: {},
path: ['repository', 'url'],
type: 'warning',
});
}

if (repositoryField.directory) {
const altRootPath = vfs.pathJoin(rootPkgPath, repositoryField.directory);
const isAltRootExist = await vfs.isPathExist(altRootPath);
Namchee marked this conversation as resolved.
Show resolved Hide resolved

if (!isAltRootExist) {
messages.push({
code: 'INVALID_REPOSITORY_VALUE',
args: {
directory: repositoryField.directory,
},
path: ['repository', 'directory'],
type: 'error',
});
}
}

return;
}

messages.push({
code: 'INVALID_REPOSITORY_VALUE',
args: {},
path: ['repository'],
type: 'warning',
})
}

/**
* @param {string[]} pkgPath
*/
Expand Down
12 changes: 12 additions & 0 deletions pkg/src/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,18 @@ export function formatMessage(m, pkg) {
case 'DEPRECATED_FIELD_JSNEXT':
// prettier-ignore
return `${c.bold(fp(m.path))} is deprecated. ${c.bold('pkg.module')} should be used instead.`
case 'INVALID_REPOSITORY_VALUE':
if (m.path.length === 1) {
return `${c.bold(fp(m.path))} must be a valid GitHub URL or an object that contains repository metadata.`;
}

if (m.path[m.path.length - 1] === 'url') {
return `${c.bold(fp(m.path))} must be a valid GitHub URL.`;
}

const dir = m.args.directory || '';

return `Cannot find package.json in ${c.bold(dir)}. Please ensure that package.json exist in the provided path.`
Namchee marked this conversation as resolved.
Show resolved Hide resolved
default:
return
}
Expand Down
10 changes: 10 additions & 0 deletions pkg/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { lintableFileExtensions } from './constants.js'
* main: string,
* module: string,
* exports: Record<string, string>,
* repository: Record<string, string> | string,
* type: 'module' | 'commonjs'
* }} Pkg
*/
Expand Down Expand Up @@ -45,6 +46,15 @@ export function stripComments(code) {
.replace(SINGLELINE_COMMENTS_RE, '')
}

// Reference: https://github.com/JoshuaKGoldberg/eslint-plugin-package-json/blob/main/src/rules/repository-shorthand.ts
const REPOSITORY_URL = /^(?:git\+)?(?:ssh:\/\/git@|http?s:\/\/)?(?:www\.)?github\.com\//
/**
* @param {string} url
*/
export function isRepositoryUrl(url) {
return REPOSITORY_URL.test(url);
}

/**
* @param {string} code
* @returns {CodeFormat}
Expand Down
Empty file.
7 changes: 7 additions & 0 deletions pkg/tests/fixtures/invalid-repository-value/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "publint-invalid-repository-value",
"version": "0.0.1",
"type": "commonjs",
"main": "./index.js",
"repository": "https://www.google.com"
}
2 changes: 2 additions & 0 deletions pkg/tests/playground.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ testFixture('deprecated-fields', [
'USE_TYPE'
])

testFixture('invalid-repository-value', ['INVALID_REPOSITORY_VALUE'])

/**
* @typedef {{
* level?: import('../index.d.ts').Options['level']
Expand Down